From: Utkarsh Gupta Date: Mon, 15 Dec 2025 12:18:10 +0000 (+0530) Subject: ceph (14.2.21-1+deb11u2) bullseye-security; urgency=high X-Git-Tag: archive/raspbian/14.2.21-1+rpi1+deb11u2^2~28 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=b01663098be9c41cedd7543edc4ff03a405b7920;p=ceph.git ceph (14.2.21-1+deb11u2) bullseye-security; urgency=high * Non-maintainer upload by the LTS team. * Add patch to check if HTTP_X_AMZ_COPY_SOURCE header is empty. (Fixes: CVE-2024-47866) (Closes: #1120797) * Add patch to fix subvolume discover during upgrade. (Fixes: CVE-2022-0670) (Closes: #1016069) [dgit import unpatched ceph 14.2.21-1+deb11u2] --- b01663098be9c41cedd7543edc4ff03a405b7920 diff --cc debian/.gitlab-ci.yml index 000000000,000000000..9815b7cd1 new file mode 100644 --- /dev/null +++ b/debian/.gitlab-ci.yml @@@ -1,0 -1,0 +1,14 @@@ ++include: ++ - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml ++ - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml ++ ++variables: ++ RELEASE: 'unstable' ++ SALSA_CI_DISABLE_APTLY: 0 ++ SALSA_CI_DISABLE_AUTOPKGTEST: 0 ++ SALSA_CI_DISABLE_BLHC: 0 ++ SALSA_CI_DISABLE_LINTIAN: 0 ++ SALSA_CI_DISABLE_PIUPARTS: 0 ++ SALSA_CI_DISABLE_REPROTEST: 1 ++ SALSA_CI_DISABLE_BUILD_PACKAGE_ALL: 1 ++ SALSA_CI_DISABLE_BUILD_PACKAGE_ANY: 1 diff --cc debian/README.Debian index 000000000,000000000..be21ad791 new file mode 100644 --- /dev/null +++ b/debian/README.Debian @@@ -1,0 -1,0 +1,120 @@@ ++## See online installation and setup documentation at ++ ++ http://ceph.com/docs/master/install/manual-deployment/ ++ ++-------- -------- -------- ++ ++## "systemd" requires manual activation of services: ++ ++ ## MON ++ # systemctl start ceph-mon ++ # systemctl enable ceph-mon ++ ++ ## OSD.0 (set other OSDs like this) ++ # systemctl start ceph-osd@0 ++ # systemctl enable ceph-osd@0 ++ ++ ## MDS ++ # systemctl start ceph-mds ++ # systemctl enable ceph-mds ++ ++ ## "ceph" meta-service (starts/stops all the above like old init script) ++ # systemctl start ceph ++ # systemctl enable ceph ++ ++ The ceph cluster can be set in the "/etc/default/ceph" file ++ by setting the CLUSTER environment variable. ++ ++-------- -------- -------- ++ ++## Upgrade procedure (0.72.2 to 0.80): ++ ++ * Read "Upgrade Sequencing" in release notes: ++ ++ http://ceph.com/docs/firefly/release-notes/ ++ ++ * Upgrade packages. ++ ++ * Restart MONs. ++ ++ * Restart all OSDs. ++ ++ * Run `ceph osd crush tunables default`. ++ ++ * (Restart MDSes). ++ ++ * Consider setting the 'hashpspool' flag on your pools (new default): ++ ++ ceph osd pool set {pool} hashpspool true ++ ++ This changes the pool to use a new hashing algorithm for the distribution of ++ Placement Groups (PGs) to OSDs. This new algorithm ensures a better distribution ++ to all OSDs. Be aware that this change will temporarly put some of your PGs into ++ "misplaced" state and cause additional I/O until all PGs are moved to their new ++ location. See http://tracker.ceph.com/issues/4128 for the details about the new ++ algorithm. ++ ++ Read more about tunables in ++ ++ http://ceph.com/docs/master/rados/operations/crush-map/#tunables ++ ++ Upgrading all OSDs and setting correct tunables is necessary to avoid the errors like: ++ ++ ## rbdmap errors: ++ libceph: mon2 192.168.0.222:6789 socket error on read ++ ++ Wrong tunables may produce the following error: ++ ++ libceph: mon0 192.168.0.222:6789 socket error on read ++ libceph: mon2 192.168.0.250:6789 feature set mismatch, my 4a042a42 < server's 2004a042a42, missing 20000000000 ++ ++ ## MDS errors: ++ one or more OSDs do not support TMAP2OMAP; upgrade OSDs before starting MDS (or downgrade MDS) ++ ++ See also: ++ ++ http://ceph.com/docs/firefly/install/upgrading-ceph/ ++ ++-------- -------- -------- ++ ++ Jerasure pool(s) will bump requirements to Linux_3.15 (not yet released) for ++ kernel CephFS and RBD clients. ++ ++-------- -------- -------- ++ ++ RBD kernel driver do not support authentication so the following setting ++ in "/etc/ceph/ceph.conf" may be used to relax client auth. requirements: ++ ++ cephx cluster require signatures = true ++ cephx service require signatures = false ++ ++-------- -------- -------- ++ ++> How to mount CephFS using fuse client from "/etc/fstab"? ++ ++ Add (and modify) the following sample to "/etc/fstab": ++ ++ mount.fuse.ceph#conf=/etc/ceph/ceph.conf,id=admin /mnt/ceph fuse _netdev,noatime,allow_other 0 0 ++ ++ This is equivalent of running ++ ++ ceph-fuse /mnt/ceph --id=admin -o noatime,allow_other ++ ++ as root. ++ ++-------- -------- -------- ++ ++ To avoid known issue with kernel FS client it is recommended to use ++ 'readdir_max_entries' mount option, for example: ++ ++ mount -t ceph 1.2.3.4:/ /mnt/ceph -o readdir_max_entries=64 ++ ++-------- -------- -------- ++ ++ Beware of "mlocate" scanning of OSD file systems. To avoid problems add ++ "/var/lib/ceph" to PRUNEPATHS in the "/etc/updatedb.conf" like in the ++ following example: ++ ++ PRUNEPATHS="/tmp /var/spool /media /mnt /var/lib/ceph" ++ ++-------- -------- -------- diff --cc debian/calc-max-parallel.sh index 000000000,000000000..3593955ca new file mode 100755 --- /dev/null +++ b/debian/calc-max-parallel.sh @@@ -1,0 -1,0 +1,32 @@@ ++#!/bin/sh ++# ++# Simple tool to calculate max parallel jobs based on ++# memory of builder. ++# ++# MDCache.cc generally runs out of RAM in 4G of memory ++# with parallel=4 ++ ++total_ram=$(grep MemTotal /proc/meminfo | awk '{ print $2 }') ++ ++sixtyfour_g=$((64*1024*1024)) ++fourtyheight_g=$((48*1024*1024)) ++thirtytwo_g=$((32*1024*1024)) ++sixteen_g=$((16*1024*1024)) ++eight_g=$((8*1024*1024)) ++four_g=$((4*1024*1024)) ++ ++if [ ${total_ram} -le ${four_g} ]; then ++ echo "--max-parallel=1" ++elif [ ${total_ram} -le ${eight_g} ]; then ++ echo "--max-parallel=2" ++elif [ ${total_ram} -le ${sixteen_g} ]; then ++ echo "--max-parallel=3" ++elif [ ${total_ram} -le ${thirtytwo_g} ]; then ++ echo "--max-parallel=6" ++elif [ ${total_ram} -le ${fourtyheight_g} ]; then ++ echo "--max-parallel=8" ++elif [ ${total_ram} -le ${sixtyfour_g} ]; then ++ echo "--max-parallel=12" ++else ++ echo "--max-parallel=16" ++fi diff --cc debian/ceph-base.ceph.init index 000000000,000000000..b538109d2 new file mode 120000 --- /dev/null +++ b/debian/ceph-base.ceph.init @@@ -1,0 -1,0 +1,1 @@@ ++../src/init-ceph diff --cc debian/ceph-base.dirs index d0b8a9e10,000000000..cf605ad44 mode 100644,000000..100644 --- a/debian/ceph-base.dirs +++ b/debian/ceph-base.dirs @@@ -1,9 -1,0 +1,9 @@@ +var/lib/ceph/bootstrap-mds +var/lib/ceph/bootstrap-mgr +var/lib/ceph/bootstrap-osd - var/lib/ceph/bootstrap-rgw +var/lib/ceph/bootstrap-rbd +var/lib/ceph/bootstrap-rbd-mirror - var/lib/ceph/tmp ++var/lib/ceph/bootstrap-rgw +var/lib/ceph/crash +var/lib/ceph/crash/posted ++var/lib/ceph/tmp diff --cc debian/ceph-base.docs index e845566c0,000000000..b43bf86b5 mode 100644,000000..100644 --- a/debian/ceph-base.docs +++ b/debian/ceph-base.docs @@@ -1,1 -1,0 +1,1 @@@ - README ++README.md diff --cc debian/ceph-base.install index 24731dc89,000000000..f1d98596b mode 100644,000000..100644 --- a/debian/ceph-base.install +++ b/debian/ceph-base.install @@@ -1,22 -1,0 +1,20 @@@ - etc/init.d/ceph ++## install from source tree +lib/systemd/system/ceph-crash.service +usr/bin/ceph-crash - usr/bin/ceph-debugpack ++usr/bin/ceph-kvstore-tool +usr/bin/ceph-run +usr/bin/crushtool +usr/bin/monmaptool +usr/bin/osdmaptool - usr/bin/ceph-kvstore-tool ++usr/lib/*/ceph/erasure-code/* ++usr/lib/*/rados-classes/* +usr/lib/ceph/ceph_common.sh - usr/lib/ceph/erasure-code/* - usr/lib/rados-classes/* +usr/sbin/ceph-create-keys +usr/share/doc/ceph/sample.ceph.conf +usr/share/man/man8/ceph-create-keys.8 - usr/share/man/man8/ceph-debugpack.8 +usr/share/man/man8/ceph-deploy.8 ++usr/share/man/man8/ceph-kvstore-tool.8 +usr/share/man/man8/ceph-run.8 +usr/share/man/man8/crushtool.8 +usr/share/man/man8/monmaptool.8 +usr/share/man/man8/osdmaptool.8 - usr/share/man/man8/ceph-kvstore-tool.8 diff --cc debian/ceph-base.postinst index 75eeb59c6,000000000..23712a8b4 mode 100644,000000..100644 --- a/debian/ceph-base.postinst +++ b/debian/ceph-base.postinst @@@ -1,61 -1,0 +1,61 @@@ +#!/bin/sh +# vim: set noet ts=8: +# postinst script for ceph +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# +# postinst configure +# old-postinst abort-upgrade +# conflictor's-postinst abort-remove in-favour +# postinst abort-remove +# deconfigured's-postinst abort-deconfigure in-favour [ ] +# +# The current action is to simply remove the mistakenly-added +# /etc/init/ceph.conf file; this could be done in any of these cases, +# although technically it will leave the system in a different state +# than the original install that included that file. So instead we +# only remove on "configure", since that's the only time we know we're +# successful in installing a newer package than the erroneous version. + +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +[ -f "/etc/default/ceph" ] && . /etc/default/ceph +[ -z "$SERVER_USER" ] && SERVER_USER=ceph +[ -z "$SERVER_GROUP" ] && SERVER_GROUP=ceph + +case "$1" in - configure) - rm -f /etc/init/ceph.conf - [ -x /sbin/start ] && start ceph-all || : - - # adjust file and directory permissions - for DIR in /var/lib/ceph/* ; do - if ! dpkg-statoverride --list $DIR >/dev/null - then - chown $SERVER_USER:$SERVER_GROUP $DIR - fi - done - ;; - abort-upgrade|abort-remove|abort-deconfigure) - : - ;; - - *) - echo "postinst called with unknown argument \`$1'" >&2 - exit 1 - ;; ++ configure) ++ rm -f /etc/init/ceph.conf ++ for DIR in `ls -1 /var/lib/ceph` ; do ++ if ! dpkg-statoverride --list /var/lib/ceph/$DIR >/dev/null; then ++ if [ -d /run/systemd/system ] && [ $DIR = 'mon' ]; then ++ # NOTE: upgrade file permissions for mon filesystem on ++ # systemd based installs only due to automatic ++ # restarting of ceph-mon daemon ++ chown -R $SERVER_USER:$SERVER_GROUP /var/lib/ceph/$DIR ++ else ++ chown $SERVER_USER:$SERVER_GROUP /var/lib/ceph/$DIR ++ fi ++ fi ++ done ++ ;; ++ abort-upgrade|abort-remove|abort-deconfigure) ++ : ++ ;; ++ *) ++ echo "postinst called with unknown argument \`$1'" >&2 ++ exit 1 ++ ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 - - diff --cc debian/ceph-base.postrm index 000000000,000000000..05091b223 new file mode 100644 --- /dev/null +++ b/debian/ceph-base.postrm @@@ -1,0 -1,0 +1,13 @@@ ++#!/bin/sh ++ ++set -e ++ ++if [ "${1}" = "purge" ] ; then ++ rm -rf /var/log/ceph ++fi ++ ++#DEBHELPER# ++ ++exit 0 ++ ++ diff --cc debian/ceph-common.install index b84a3164d,000000000..9f6d385c2 mode 100755,000000..100755 --- a/debian/ceph-common.install +++ b/debian/ceph-common.install @@@ -1,44 -1,0 +1,44 @@@ - #! /usr/bin/dh-exec --with=install - - etc/bash_completion.d/ceph - etc/bash_completion.d/rados - etc/bash_completion.d/radosgw-admin - etc/bash_completion.d/rbd ++#!/usr/bin/dh-exec --with=install ++usr/share/bash-completion/completions/ceph ++usr/share/bash-completion/completions/rados ++usr/share/bash-completion/completions/radosgw-admin ++usr/share/bash-completion/completions/rbd +lib/systemd/system/ceph.target +lib/systemd/system/rbdmap.service ++etc/default/ceph +usr/bin/ceph +usr/bin/ceph-authtool +usr/bin/ceph-conf +usr/bin/ceph-dencoder +usr/bin/ceph-rbdnamer +usr/bin/ceph-syn +usr/bin/cephfs-data-scan +usr/bin/cephfs-journal-tool +usr/bin/cephfs-table-tool +usr/bin/rados +usr/bin/radosgw-admin +usr/bin/rbd +usr/bin/rbdmap +usr/bin/rbd-replay* +usr/bin/ceph-post-file +usr/sbin/mount.ceph sbin - usr/lib/ceph/compressor/* - usr/lib/ceph/crypto/* [amd64] ++usr/lib/*/ceph/compressor/* ++usr/lib/*/ceph/crypto/* [amd64] +usr/share/man/man8/ceph-authtool.8 +usr/share/man/man8/ceph-conf.8 +usr/share/man/man8/ceph-dencoder.8 +usr/share/man/man8/ceph-rbdnamer.8 +usr/share/man/man8/ceph-syn.8 +usr/share/man/man8/ceph-post-file.8 +usr/share/man/man8/ceph.8 +usr/share/man/man8/mount.ceph.8 +usr/share/man/man8/rados.8 +usr/share/man/man8/radosgw-admin.8 +usr/share/man/man8/rbd.8 +usr/share/man/man8/rbdmap.8 +usr/share/man/man8/rbd-replay*.8 +usr/share/ceph/known_hosts_drop.ceph.com +usr/share/ceph/id_rsa_drop.ceph.com +usr/share/ceph/id_rsa_drop.ceph.com.pub +etc/ceph/rbdmap +lib/udev/rules.d/50-rbd.rules diff --cc debian/ceph-common.lintian-overrides index 000000000,000000000..d4041ca8e new file mode 100644 --- /dev/null +++ b/debian/ceph-common.lintian-overrides @@@ -1,0 -1,0 +1,2 @@@ ++# False-positives: ++spelling-error-in-binary * tEH the diff --cc debian/ceph-common.manpages index 000000000,000000000..643fa2a41 new file mode 100644 --- /dev/null +++ b/debian/ceph-common.manpages @@@ -1,0 -1,0 +1,1 @@@ ++debian/man/ceph-crush-location.1 diff --cc debian/ceph-common.postinst index b9d382aaa,000000000..5535e9dee mode 100644,000000..100644 --- a/debian/ceph-common.postinst +++ b/debian/ceph-common.postinst @@@ -1,113 -1,0 +1,140 @@@ +#!/bin/sh +# vim: set noet ts=8: - # postinst script for ceph-mds ++# postinst script for ceph-common +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# +# postinst configure +# old-postinst abort-upgrade +# conflictor's-postinst abort-remove in-favour +# postinst abort-remove +# deconfigured's-postinst abort-deconfigure in-favour [ ] +# + +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +# Let the admin override these distro-specified defaults. This is NOT +# recommended! +[ -f "/etc/default/ceph" ] && . /etc/default/ceph + +[ -z "$SERVER_HOME" ] && SERVER_HOME=/var/lib/ceph +[ -z "$SERVER_USER" ] && SERVER_USER=ceph +[ -z "$SERVER_NAME" ] && SERVER_NAME="Ceph storage service" +[ -z "$SERVER_GROUP" ] && SERVER_GROUP=ceph +[ -z "$SERVER_UID" ] && SERVER_UID=64045 # alloc by Debian base-passwd maintainer +[ -z "$SERVER_GID" ] && SERVER_GID=$SERVER_UID + + +# Groups that the user will be added to, if undefined, then none. +[ -z "$SERVER_ADDGROUP" ] && SERVER_ADDGROUP= + ++# Custom dpkg-maintscript-helper type function to deal with ++# nested /etc/default/ceph/ceph ++finish_mv_ceph_defaults() { ++ rm -rf "/etc/default/ceph.dpkg-backup/ceph.dpkg-remove" ++ ++ [ -e "/etc/default/ceph.dpkg-backup/ceph" ] || return 0 ++ ++ echo "Preserving user changes to /etc/default/ceph (renamed from /etc/default/ceph/ceph)..." ++ if [ -f "/etc/default/ceph" ]; then ++ mv -f "/etc/default/ceph" "/etc/default/ceph.dpkg-new" ++ fi ++ mv -f "/etc/default/ceph.dpkg-backup/ceph" "/etc/default/ceph" ++} ++ +case "$1" in + configure) + # create user to avoid running server as root + # 1. create group if not existing + if ! getent group | grep -q "^$SERVER_GROUP:" ; then - echo -n "Adding group $SERVER_GROUP.." ++ echo -n "Adding group $SERVER_GROUP.." + addgroup --quiet --system --gid $SERVER_GID \ + $SERVER_GROUP 2>/dev/null ||true - echo "..done" ++ echo "..done" + fi + # 2. create user if not existing + if ! getent passwd | grep -q "^$SERVER_USER:"; then - echo -n "Adding system user $SERVER_USER.." ++ echo -n "Adding system user $SERVER_USER.." + adduser --quiet \ + --system \ + --no-create-home \ + --disabled-password \ + --uid $SERVER_UID \ + --gid $SERVER_GID \ ++ --home $SERVER_HOME \ + $SERVER_USER 2>/dev/null || true - echo "..done" ++ echo "..done" + fi + # 3. adjust passwd entry + echo -n "Setting system user $SERVER_USER properties.." + usermod -c "$SERVER_NAME" \ + -d $SERVER_HOME \ + -g $SERVER_GROUP \ + $SERVER_USER ++ + # Unlock $SERVER_USER in case it is locked from an uninstall + if [ -f /etc/shadow ]; then + usermod -U -e '' $SERVER_USER + else - usermod -U $SERVER_USER ++ usermod -U $SERVER_USER + fi + echo "..done" + + # 5. adjust file and directory permissions + if ! dpkg-statoverride --list $SERVER_HOME >/dev/null + then + chown $SERVER_USER:$SERVER_GROUP $SERVER_HOME + chmod u=rwx,g=rx,o= $SERVER_HOME + fi + if ! dpkg-statoverride --list /var/log/ceph >/dev/null + then + chown -R $SERVER_USER:$SERVER_GROUP /var/log/ceph + # members of group ceph can log here, but cannot remove + # others' files. non-members cannot read any logs. + chmod u=rwx,g=rwxs,o=t /var/log/ceph + fi + + # 6. fix /var/run/ceph + if [ -d /var/run/ceph ]; then + echo -n "Fixing /var/run/ceph ownership.." + chown $SERVER_USER:$SERVER_GROUP /var/run/ceph + echo "..done" + fi + + # create /run/ceph. fail softly if systemd isn't present or + # something. + [ -x /bin/systemd-tmpfiles ] && systemd-tmpfiles --create || true ++ ++ # Complete renames of /etc/default/ceph ++ if [ -n "$2" ] && ++ dpkg --compare-versions -- "$2" le-nl 10.2.1-0ubuntu1; then ++ finish_mv_ceph_defaults ++ # Preserve dpkg-backup directory if it still contains ++ # any file ++ if ! ls -1qA "/etc/default/ceph.dpkg-backup" | grep -q . ; then ++ rm -rf "/etc/default/ceph.dpkg-backup" ++ fi ++ fi + ;; + abort-upgrade|abort-remove|abort-deconfigure) + : + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --cc debian/ceph-common.postrm index e6a97543a,000000000..e4051180d mode 100644,000000..100644 --- a/debian/ceph-common.postrm +++ b/debian/ceph-common.postrm @@@ -1,56 -1,0 +1,74 @@@ +#!/bin/sh +# postrm script for ceph-common +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `remove' +# * `purge' +# * `upgrade' +# * `failed-upgrade' +# * `abort-install' +# * `abort-install' +# * `abort-upgrade' +# * `disappear' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + ++# Custom dpkg-maintscript-helper type function to deal with ++# nested /etc/default/ceph/ceph ++abort_mv_ceph_defaults() { ++ if [ -e "/etc/default/ceph.dpkg-backup/ceph.dpkg-remove" ]; then ++ echo "Reinstalling /etc/default/ceph/ceph that was moved away" ++ mv "/etc/default/ceph.dpkg-backup" "/etc/default/ceph" ++ mv "/etc/default/ceph/ceph.dpkg-remove" "/etc/default/ceph/ceph" ++ fi ++} ++ +case "$1" in + remove) + ;; + + purge) + [ -f "/etc/default/ceph" ] && . /etc/default/ceph + [ -z "$SERVER_USER" ] && SERVER_USER=ceph + - rm -rf /var/log/ceph - rm -rf /etc/ceph ++ rm -rf /var/log/ceph ++ rm -rf /etc/ceph + + if [ -f /etc/shadow ]; then + usermod -L -e 1 $SERVER_USER + else + usermod -L $SERVER_USER + fi ++ ++ ;; ++ ++ abort-install|abort-upgrade) ++ if [ -n "$2" ] && ++ dpkg --compare-versions -- "$2" le-nl 10.2.1-0ubuntu1; then ++ abort_mv_ceph_defaults ++ fi + ;; + - upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) ++ upgrade|failed-upgrade|disappear) + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + diff --cc debian/ceph-common.preinst index 000000000,000000000..ef14f1eb1 new file mode 100644 --- /dev/null +++ b/debian/ceph-common.preinst @@@ -1,0 -1,0 +1,29 @@@ ++#!/bin/sh ++ ++set -e ++ ++# Custom dpkg-maintscript-helper type function to deal with ++# nested /etc/default/ceph/ceph ++prepare_mv_ceph_defaults() { ++ local md5sum old_md5sum ++ md5sum="$(md5sum "/etc/default/ceph/ceph" | sed -e 's/ .*//')" ++ old_md5sum="$(dpkg-query -W -f='${Conffiles}' "ceph-common" | \ ++ sed -n -e "\'^ /etc/default/ceph/ceph ' { s/ obsolete$//; s/.* //; p }")" ++ if [ "$md5sum" = "$old_md5sum" ]; then ++ mv -f "/etc/default/ceph/ceph" "/etc/default/ceph/ceph.dpkg-remove" ++ mv -f "/etc/default/ceph" "/etc/default/ceph.dpkg-backup" ++ fi ++} ++ ++case "$1" in ++ upgrade|install) ++ if [ -d /etc/default/ceph ] && [ -n "$2" ] && ++ dpkg --compare-versions -- "$2" le-nl 10.2.1-0ubuntu1; then ++ prepare_mv_ceph_defaults ++ fi ++ ;; ++esac ++ ++#DEBHELPER# ++ ++exit 0 diff --cc debian/ceph-common.rbdmap.init index 000000000,000000000..b2de0ce8b new file mode 120000 --- /dev/null +++ b/debian/ceph-common.rbdmap.init @@@ -1,0 -1,0 +1,1 @@@ ++../src/init-rbdmap diff --cc debian/ceph-fs-common.install index 000000000,000000000..a4f0bab50 new file mode 100644 --- /dev/null +++ b/debian/ceph-fs-common.install @@@ -1,0 -1,0 +1,4 @@@ ++usr/bin/cephfs ++usr/sbin/mount.ceph sbin ++usr/share/man/man8/cephfs.8 ++usr/share/man/man8/mount.ceph.8 diff --cc debian/ceph-fuse.lintian-overrides index 000000000,000000000..d4041ca8e new file mode 100644 --- /dev/null +++ b/debian/ceph-fuse.lintian-overrides @@@ -1,0 -1,0 +1,2 @@@ ++# False-positives: ++spelling-error-in-binary * tEH the diff --cc debian/ceph-fuse.manpages index 000000000,000000000..e4c9c231e new file mode 100644 --- /dev/null +++ b/debian/ceph-fuse.manpages @@@ -1,0 -1,0 +1,1 @@@ ++debian/man/mount.fuse.ceph.8 diff --cc debian/ceph-mds.lintian-overrides index 000000000,000000000..c88395441 new file mode 100644 --- /dev/null +++ b/debian/ceph-mds.lintian-overrides @@@ -1,0 -1,0 +1,13 @@@ ++# False-positives: ++spelling-error-in-binary * tEH the ++ ++# Ceph upstart configuration don't have equivalent init scripts ++ceph-mds: init.d-script-not-marked-as-conffile etc/init.d/ceph-mds-all ++ceph-mds: init.d-script-not-included-in-package etc/init.d/ceph-mds-all ++ceph-mds: init.d-script-not-marked-as-conffile etc/init.d/ceph-mds ++ceph-mds: init.d-script-not-included-in-package etc/init.d/ceph-mds ++ceph-mds: init.d-script-not-marked-as-conffile etc/init.d/ceph-mds-all-starter ++ceph-mds: init.d-script-not-included-in-package etc/init.d/ceph-mds-all-starter ++ceph-mds: postrm-does-not-call-updaterc.d-for-init.d-script etc/init.d/ceph-mds-all ++ceph-mds: postrm-does-not-call-updaterc.d-for-init.d-script etc/init.d/ceph-mds ++ceph-mds: postrm-does-not-call-updaterc.d-for-init.d-script etc/init.d/ceph-mds-all-starter diff --cc debian/ceph-mds.postinst index b69efedaa,000000000..3ae908a99 mode 100644,000000..100644 --- a/debian/ceph-mds.postinst +++ b/debian/ceph-mds.postinst @@@ -1,51 -1,0 +1,47 @@@ +#!/bin/sh +# vim: set noet ts=8: +# postinst script for ceph-mds +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# +# postinst configure +# old-postinst abort-upgrade +# conflictor's-postinst abort-remove in-favour +# postinst abort-remove +# deconfigured's-postinst abort-deconfigure in-favour [ ] +# + +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +[ -f "/etc/default/ceph" ] && . /etc/default/ceph +[ -z "$SERVER_USER" ] && SERVER_USER=ceph +[ -z "$SERVER_GROUP" ] && SERVER_GROUP=ceph + +case "$1" in - configure) - [ -x /sbin/start ] && start ceph-mds-all || : - - if ! dpkg-statoverride --list /var/lib/ceph/mds >/dev/null - then - chown $SERVER_USER:$SERVER_GROUP /var/lib/ceph/mds - fi - ;; - abort-upgrade|abort-remove|abort-deconfigure) - : - ;; - - *) - echo "postinst called with unknown argument \`$1'" >&2 - exit 1 - ;; ++ configure) ++ if ! dpkg-statoverride --list /var/lib/ceph/mds >/dev/null; ++ then ++ chown $SERVER_USER:$SERVER_GROUP /var/lib/ceph/mds ++ fi ++ ;; ++ abort-upgrade|abort-remove|abort-deconfigure) ++ : ++ ;; ++ ++ *) ++ echo "postinst called with unknown argument \`$1'" >&2 ++ exit 1 ++ ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 - - diff --cc debian/ceph-mgr-k8sevents.install index 000000000,000000000..734da94ca new file mode 100644 --- /dev/null +++ b/debian/ceph-mgr-k8sevents.install @@@ -1,0 -1,0 +1,1 @@@ ++usr/share/ceph/mgr/k8sevents diff --cc debian/ceph-mgr-k8sevents.postinst index 000000000,000000000..4ab578fd1 new file mode 100644 --- /dev/null +++ b/debian/ceph-mgr-k8sevents.postinst @@@ -1,0 -1,0 +1,43 @@@ ++#!/bin/sh ++# vim: set noet ts=8: ++# postinst script for ceph-mgr-k8sevents ++# ++# see: dh_installdeb(1) ++ ++set -e ++ ++# summary of how this script can be called: ++# ++# postinst configure ++# old-postinst abort-upgrade ++# conflictor's-postinst abort-remove in-favour ++# postinst abort-remove ++# deconfigured's-postinst abort-deconfigure in-favour [ ] ++# ++ ++# for details, see http://www.debian.org/doc/debian-policy/ or ++# the debian-policy package ++ ++case "$1" in ++ configure) ++ # attempt to load the plugin if the mgr is running ++ deb-systemd-invoke try-restart ceph-mgr.target ++ ;; ++ abort-upgrade|abort-remove|abort-deconfigure) ++ : ++ ;; ++ ++ *) ++ echo "postinst called with unknown argument \`$1'" >&2 ++ exit 1 ++ ;; ++esac ++ ++# dh_installdeb will replace this with shell code automatically ++# generated by other debhelper scripts. ++ ++#DEBHELPER# ++ ++exit 0 ++ ++ diff --cc debian/ceph-mgr.install index aa372cea5,000000000..2d82c0df3 mode 100644,000000..100644 --- a/debian/ceph-mgr.install +++ b/debian/ceph-mgr.install @@@ -1,29 -1,0 +1,28 @@@ +lib/systemd/system/ceph-mgr* +usr/bin/ceph-mgr - usr/share/ceph/mgr/alerts +usr/share/ceph/mgr/ansible +usr/share/ceph/mgr/balancer +usr/share/ceph/mgr/crash +usr/share/ceph/mgr/deepsea +usr/share/ceph/mgr/devicehealth +usr/share/ceph/mgr/influx +usr/share/ceph/mgr/insights +usr/share/ceph/mgr/iostat +usr/share/ceph/mgr/localpool +usr/share/ceph/mgr/mgr_module.* +usr/share/ceph/mgr/mgr_util.* - usr/share/ceph/mgr/orchestrator_cli +usr/share/ceph/mgr/orchestrator.* ++usr/share/ceph/mgr/orchestrator_cli +usr/share/ceph/mgr/osd_perf_query +usr/share/ceph/mgr/pg_autoscaler +usr/share/ceph/mgr/progress +usr/share/ceph/mgr/prometheus +usr/share/ceph/mgr/rbd_support +usr/share/ceph/mgr/restful +usr/share/ceph/mgr/selftest +usr/share/ceph/mgr/status - usr/share/ceph/mgr/test_orchestrator +usr/share/ceph/mgr/telegraf +usr/share/ceph/mgr/telemetry ++usr/share/ceph/mgr/test_orchestrator +usr/share/ceph/mgr/volumes +usr/share/ceph/mgr/zabbix diff --cc debian/ceph-mon.postinst index b33f34b6b,000000000..454ffc141 mode 100644,000000..100644 --- a/debian/ceph-mon.postinst +++ b/debian/ceph-mon.postinst @@@ -1,45 -1,0 +1,46 @@@ ++#!/bin/bash +# vim: set noet ts=8: +# postinst script for ceph-mon +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# +# postinst configure +# old-postinst abort-upgrade +# conflictor's-postinst abort-remove in-favour +# postinst abort-remove +# deconfigured's-postinst abort-deconfigure in-favour [ ] +# + +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +[ -f "/etc/default/ceph" ] && . /etc/default/ceph +[ -z "$SERVER_USER" ] && SERVER_USER=ceph +[ -z "$SERVER_GROUP" ] && SERVER_GROUP=ceph + +case "$1" in + configure) - [ -x /sbin/start ] && start ceph-mon-all || : ++ : + ;; + abort-upgrade|abort-remove|abort-deconfigure) + : + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + diff --cc debian/ceph-osd.install index 39420ca41,000000000..5a23c19d1 mode 100644,000000..100644 --- a/debian/ceph-osd.install +++ b/debian/ceph-osd.install @@@ -1,22 -1,0 +1,20 @@@ ++debian/udev/* lib/udev/rules.d ++etc/sudoers.d/ceph-osd-smartctl ++etc/sysctl.d/30-ceph-osd.conf +lib/systemd/system/ceph-osd* +lib/systemd/system/ceph-volume@.service +usr/bin/ceph-bluestore-tool +usr/bin/ceph-clsinfo +usr/bin/ceph-objectstore-tool - usr/bin/ceph-osdomap-tool +usr/bin/ceph-osd - usr/bin/ceph_objectstore_bench ++usr/bin/ceph-osdomap-tool +usr/lib/ceph/ceph-osd-prestart.sh - usr/lib/libos_tp.so* - usr/lib/libosd_tp.so* ++usr/lib/python*/dist-packages/ceph_volume-* ++usr/lib/python*/dist-packages/ceph_volume/* +usr/sbin/ceph-volume +usr/sbin/ceph-volume-systemd - usr/lib/python*/dist-packages/ceph_volume/* - usr/lib/python*/dist-packages/ceph_volume-* ++usr/share/man/man8/ceph-bluestore-tool.8 +usr/share/man/man8/ceph-clsinfo.8 - usr/share/man/man8/ceph-volume.8 - usr/share/man/man8/ceph-volume-systemd.8 +usr/share/man/man8/ceph-osd.8 - usr/share/man/man8/ceph-bluestore-tool.8 - etc/sysctl.d/30-ceph-osd.conf - etc/sudoers.d/ceph-osd-smartctl ++usr/share/man/man8/ceph-volume-systemd.8 ++usr/share/man/man8/ceph-volume.8 diff --cc debian/ceph.NEWS index 000000000,000000000..ee9db2f22 new file mode 100644 --- /dev/null +++ b/debian/ceph.NEWS @@@ -1,0 -1,0 +1,180 @@@ ++ceph (10.2.5-1) unstable; urgency=medium ++ ++ ## Upgrades from Debian Jessie ++ ++ Online upgrades from Ceph versions prior to Hammer (0.94.x) are not ++ supported by upstream. As Debian Jessie has Ceph Firefly (0.80.x) an ++ online upgrade from Jessie to Stretch is not possible. You have to first ++ shutdown all Ceph daemons on all nodes, upgrade everything to the new ++ version and start all daemons again. ++ ++ Ceph daemons are not automatically restarted on upgrade to minimize ++ disruption. You have to manually restart them after the upgrade. ++ ++ -- Gaudenz Steinlin Sun, 08 Jan 2017 14:57:35 +0100 ++ ++ceph (9.2.0-1) experimental; urgency=medium ++ ++ ## systemd Enablement ++ ++ For all distributions that support systemd (Debian Jessie 8.x, ++ Ubuntu >= 16.04), Ceph daemons are now managed using upstream provided ++ systemd files instead of the legacy sysvinit scripts or distro provided ++ systemd files. For example: ++ ++ systemctl start ceph.target # start all daemons ++ systemctl status ceph-osd@12 # check status of osd.12 ++ ++ To upgrade existing deployments that use the older systemd service ++ configurations (Ubuntu >= 15.04, Debian >= Jessie), you need to switch ++ to using the new ceph-mon@ service: ++ ++ systemctl stop ceph-mon ++ systemctl disable ceph-mon ++ ++ systemctl start ceph-mon@`hostname` ++ systemctl enable ceph-mon@`hostname` ++ ++ and also enable the ceph target post upgrade: ++ ++ systemctl enable ceph.target ++ ++ The main notable distro that is *not* using systemd is Ubuntu 14.04 ++ (The next Ubuntu LTS, 16.04, will use systemd instead of upstart). ++ ++ ## Ceph daemons no longer run as root ++ ++ Ceph daemons now run as user and group 'ceph' by default. The ++ ceph user has a static UID assigned by Debian to ensure consistency ++ across servers within a Ceph deployment. ++ ++ If your systems already have a ceph user, upgrading the package will cause ++ problems. We suggest you first remove or rename the existing 'ceph' user ++ and 'ceph' group before upgrading. ++ ++ When upgrading, administrators have two options: ++ ++ 1. Add the following line to 'ceph.conf' on all hosts: ++ ++ setuser match path = /var/lib/ceph/$type/$cluster-$id ++ ++ This will make the Ceph daemons run as root (i.e., not drop ++ privileges and switch to user ceph) if the daemon's data ++ directory is still owned by root. Newly deployed daemons will ++ be created with data owned by user ceph and will run with ++ reduced privileges, but upgraded daemons will continue to run as ++ root. ++ ++ 2. Fix the data ownership during the upgrade. This is the ++ preferred option, but it is more work and can be very time ++ consuming. The process for each host is to: ++ ++ 1. Upgrade the ceph package. This creates the ceph user and group. For ++ example: ++ ++ apt-get install ceph ++ ++ NOTE: the permissions on /var/lib/ceph/mon will be set to ceph:ceph ++ as part of the package upgrade process on existing *systemd* ++ based installations; the ceph-mon systemd service will be ++ automatically restarted as part of the upgrade. All other ++ filesystem permissions on systemd based installs will ++ remain unmodified by the upgrade. ++ ++ 2. Stop the daemon(s): ++ ++ systemctl stop ceph-osd@* # debian, ubuntu >= 15.04 ++ stop ceph-all # ubuntu 14.04 ++ ++ 3. Fix the ownership: ++ ++ chown -R ceph:ceph /var/lib/ceph ++ ++ 4. Restart the daemon(s): ++ ++ start ceph-all # ubuntu 14.04 ++ systemctl start ceph.target # debian, ubuntu >= 15.04 ++ ++ Alternatively, the same process can be done with a single daemon ++ type, for example by stopping only monitors and chowning only ++ '/var/lib/ceph/osd'. ++ ++ ## KeyValueStore OSD on-disk format changes ++ ++ The on-disk format for the experimental KeyValueStore OSD backend has ++ changed. You will need to remove any OSDs using that backend before you ++ upgrade any test clusters that use it. ++ ++ ## Deprecated commands ++ ++ 'ceph scrub', 'ceph compact' and 'ceph sync force' are now DEPRECATED. ++ Users should instead use 'ceph mon scrub', 'ceph mon compact' and ++ 'ceph mon sync force'. ++ ++ ## Full pool behaviour ++ ++ When a pool quota is reached, librados operations now block indefinitely, ++ the same way they do when the cluster fills up. (Previously they would ++ return -ENOSPC). By default, a full cluster or pool will now block. If ++ your librados application can handle ENOSPC or EDQUOT errors gracefully, ++ you can get error returns instead by using the new librados ++ OPERATION_FULL_TRY flag. ++ ++ -- James Page Mon, 30 Nov 2015 09:23:09 +0000 ++ ++ceph (0.80.9-2) unstable; urgency=medium ++ ++ ## CRUSH fixes in 0.80.9 ++ ++ The 0.80.9 point release fixes several issues with CRUSH that trigger excessive ++ data migration when adjusting OSD weights. These are most obvious when a very ++ small weight change (e.g., a change from 0 to .01) triggers a large amount of ++ movement, but the same set of bugs can also lead to excessive (though less ++ noticeable) movement in other cases. ++ ++ However, because the bug may already have affected your cluster, fixing it ++ may trigger movement back to the more correct location. For this reason, you ++ must manually opt-in to the fixed behavior. ++ ++ In order to set the new tunable to correct the behavior: ++ ++ ceph osd crush set-tunable straw_calc_version 1 ++ ++ Note that this change will have no immediate effect. However, from this ++ point forward, any ‘straw’ bucket in your CRUSH map that is adjusted will get ++ non-buggy internal weights, and that transition may trigger some rebalancing. ++ ++ You can estimate how much rebalancing will eventually be necessary on your ++ cluster with: ++ ++ ceph osd getcrushmap -o /tmp/cm ++ crushtool -i /tmp/cm --num-rep 3 --test --show-mappings > /tmp/a 2>&1 ++ crushtool -i /tmp/cm --set-straw-calc-version 1 -o /tmp/cm2 ++ crushtool -i /tmp/cm2 --reweight -o /tmp/cm2 ++ crushtool -i /tmp/cm2 --num-rep 3 --test --show-mappings > /tmp/b 2>&1 ++ wc -l /tmp/a # num total mappings ++ diff -u /tmp/a /tmp/b | grep -c ^+ # num changed mappings ++ ++ Divide the total number of lines in /tmp/a with the number of lines ++ changed. We've found that most clusters are under 10%. ++ ++ You can force all of this rebalancing to happen at once with: ++ ++ ceph osd crush reweight-all ++ ++ Otherwise, it will happen at some unknown point in the future when ++ CRUSH weights are next adjusted. ++ ++ ## Mapping rbd devices with rbdmap on systemd systems ++ ++ If you have setup rbd mappings in /etc/ceph/rbdmap and corresponding mounts ++ in /etc/fstab things might break with systemd because systemd waits for the ++ rbd device to appear before the legacy rbdmap init file has a chance to run ++ and drops into emergency mode if it times out. ++ ++ This can be fixed by adding the nofail option in /etc/fstab to all rbd ++ backed mount points. With this systemd does not wait for the device and ++ proceeds with the boot process. After rbdmap mapped the device, systemd ++ detects the new device and mounts the file system. ++ ++ -- Gaudenz Steinlin Mon, 04 May 2015 22:49:48 +0200 diff --cc debian/ceph.lintian-overrides index 000000000,000000000..99b3db84b new file mode 100644 --- /dev/null +++ b/debian/ceph.lintian-overrides @@@ -1,0 -1,0 +1,1 @@@ ++empty-binary-package diff --cc debian/changelog index 9c6b96359,000000000..9349c7805 mode 100644,000000..100644 --- a/debian/changelog +++ b/debian/changelog @@@ -1,877 -1,0 +1,1536 @@@ - ceph (14.2.21-1) stable; urgency=medium ++ceph (14.2.21-1+deb11u2) bullseye-security; urgency=high + - * New upstream release ++ * Non-maintainer upload by the LTS team. ++ * Add patch to check if HTTP_X_AMZ_COPY_SOURCE header is empty. ++ (Fixes: CVE-2024-47866) (Closes: #1120797) ++ * Add patch to fix subvolume discover during upgrade. ++ (Fixes: CVE-2022-0670) (Closes: #1016069) + - -- Ceph Release Team Thu, 13 May 2021 17:23:05 +0000 ++ -- Utkarsh Gupta Mon, 15 Dec 2025 17:48:10 +0530 + - ceph (14.2.20-1) stable; urgency=medium ++ceph (14.2.21-1+deb11u1) bullseye-security; urgency=medium + - * New upstream release ++ [ Thomas Goirand ] ++ * CVE-2022-3650: privilege escalation from the ceph user to root. Applied ++ upstream patches (Closes: #1024932). + - -- Ceph Release Team Mon, 19 Apr 2021 14:11:13 +0000 ++ [ Bastien Roucariès ] ++ * CVE-2021-3979: ++ A key length flaw was found. An attacker can exploit the ++ fact that the key length is incorrectly passed in an ++ encryption algorithm to create a non random key, ++ which is weaker and can be exploited for loss of ++ confidentiality and integrity on encrypted disks. ++ * CVE-2023-43040 rgw: Fix bucket validation against POST policies ++ (Closes: #1053690) ++ * CVE-2025-52555: an unprivileged user can escalate to root ++ privileges in a ceph-fuse mounted CephFS by chmod 777 ++ a directory owned by root to gain access. The result ++ of this is that a user could read, write and execute ++ to any directory owned by root as long as they chmod ++ 777 it. This impacts confidentiality, integrity, and availability. ++ (Closes: #1108410) + - ceph (14.2.19-1) stable; urgency=medium ++ -- Bastien Roucariès Mon, 22 Sep 2025 22:55:44 +0200 + - * New upstream release ++ceph (14.2.21-1) unstable; urgency=high + - -- Ceph Release Team Tue, 30 Mar 2021 16:19:15 +0000 ++ * New upstream release, resolving these: ++ - CVE-2021-3509: Cross Site Scripting via token Cookie (Closes: #988888). ++ - CVE-2021-3524: injection of HTTP headers via a CORS ExposeHeader tag in ++ the Ceph Storage RadosGW (Closes: #988889). ++ - CVE-2021-3531: RadosGW denial of service (crash) (Closes: #988890). + - ceph (14.2.18-1) stable; urgency=medium ++ -- Thomas Goirand Thu, 27 May 2021 12:04:21 +0200 + - * New upstream release ++ceph (14.2.20-2) unstable; urgency=medium + - -- Ceph Release Team Mon, 15 Mar 2021 17:46:19 +0000 ++ * Add allow-bgp-to-host.patch. + - ceph (14.2.17-1) stable; urgency=medium ++ -- Thomas Goirand Wed, 21 Apr 2021 10:02:07 +0200 + - * New upstream release ++ceph (14.2.20-1) unstable; urgency=medium + - -- Ceph Release Team Thu, 11 Mar 2021 17:07:30 +0000 ++ * New upstream point release (Closes: #986173): ++ - Fixes CVE-2021-20288: Unauthorized global_id reuse in cephx ++ (Closes: #986974) ++ * Remove "rm -rf /etc/ceph" in ceph-base.postinst (Closes: #987192). + - ceph (14.2.16-1) stable; urgency=medium ++ -- Thomas Goirand Tue, 20 Apr 2021 13:00:13 +0200 + - * New upstream release ++ceph (14.2.18-1) unstable; urgency=medium + - -- Ceph Release Team Wed, 16 Dec 2020 17:34:57 +0000 ++ * New upstream point release. ++ * Refreshed 32bit-avoid-size_t.patch. ++ * Rebased bluefs-use-uint64_t-for-len.patch. ++ * Refreshed 32bit-avoid-size_t.patch. ++ * Refreshed riscv64-link-pthread.patch. ++ * Rebased ++ cmake_define_BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT_for_Boost.Asio_users.patch ++ * Refreshed another-cmakelists-fix.patch. ++ * Removed allow-binding-on-lo.patch applied upstream. + - ceph (14.2.15-1) stable; urgency=medium ++ -- Thomas Goirand Wed, 24 Mar 2021 12:49:20 +0100 + - * New upstream release ++ceph (14.2.16-2) unstable; urgency=medium + - -- Ceph Release Team Mon, 23 Nov 2020 18:30:13 +0000 ++ * Add fix-ceph-osd-systemd-target.patch. + - ceph (14.2.14-1) stable; urgency=medium ++ -- Thomas Goirand Thu, 28 Jan 2021 16:45:23 +0100 + - * New upstream release ++ceph (14.2.16-1) unstable; urgency=medium + - -- Ceph Release Team Tue, 17 Nov 2020 18:10:08 +0000 - - ceph (14.2.13-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Fri, 30 Oct 2020 14:54:35 +0000 - - ceph (14.2.12-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Mon, 19 Oct 2020 20:19:19 +0000 - - ceph (14.2.11-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Mon, 10 Aug 2020 20:15:20 +0000 - - ceph (14.2.10-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Thu, 25 Jun 2020 17:32:29 +0000 - - ceph (14.2.9-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Thu, 09 Apr 2020 16:17:27 +0000 - - ceph (14.2.8-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Mon, 02 Mar 2020 17:49:19 +0000 - - ceph (14.2.7-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Fri, 31 Jan 2020 17:07:50 +0000 - - ceph (14.2.6-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Wed, 08 Jan 2020 18:36:52 +0000 - - ceph (14.2.5-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Fri, 06 Dec 2019 16:42:32 +0000 - - ceph (14.2.4-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Fri, 13 Sep 2019 14:07:41 -0400 - - ceph (14.2.3-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Tue, 03 Sep 2019 13:19:56 +0000 - - ceph (14.2.2-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Wed, 17 Jul 2019 15:12:34 +0000 - - ceph (14.2.1-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Thu, 25 Apr 2019 18:15:46 +0000 - - ceph (14.2.0-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Mon, 18 Mar 2019 10:08:27 +0000 - - ceph (14.1.1-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Mon, 11 Mar 2019 16:42:54 +0000 - - ceph (14.1.0-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Fri, 22 Feb 2019 18:07:06 +0000 - - ceph (13.1.0-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Thu, 03 May 2018 17:57:32 +0000 - - ceph (12.1.2-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Tue, 01 Aug 2017 17:55:37 +0000 - - ceph (12.1.1-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Mon, 17 Jul 2017 16:55:59 +0000 - - ceph (12.1.0-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Thu, 22 Jun 2017 15:43:47 +0000 - - ceph (12.0.3-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Tue, 16 May 2017 12:42:53 +0000 - - ceph (12.0.2-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Thu, 20 Apr 2017 19:59:57 +0000 - - ceph (12.0.1-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Fri, 24 Mar 2017 15:47:57 +0000 - - ceph (12.0.0-1) stable; urgency=medium - - * New upstream release - - -- Ceph Release Team Wed, 08 Feb 2017 13:57:30 +0000 - - ceph (11.1.0-1) stable; urgency=medium - - * New upstream release - - -- Alfredo Deza Mon, 12 Dec 2016 18:27:51 +0000 - - ceph (11.0.2-1) stable; urgency=medium - - * New upstream release - - -- Alfredo Deza Mon, 17 Oct 2016 11:16:49 +0000 - - ceph (11.0.1-1) stable; urgency=medium - - * New upstream release - - -- Alfredo Deza Tue, 11 Oct 2016 16:27:56 +0000 - - ceph (11.0.0-1) stable; urgency=low - - * New upstream release - - -- Sage Weil Tue, 28 Jun 2016 11:41:16 -0400 - - ceph (10.2.0-1) stable; urgency=medium - - * New upstream release - - -- Alfredo Deza Wed, 20 Apr 2016 11:29:47 +0000 - - ceph (10.1.2-1) stable; urgency=medium - - * New upstream release - - -- Alfredo Deza Tue, 12 Apr 2016 17:42:55 +0000 - - ceph (10.1.1-1) stable; urgency=medium - - * New upstream release - - -- Alfredo Deza Wed, 06 Apr 2016 00:45:18 +0000 - - ceph (10.1.0-1) stable; urgency=medium - - * New upstream release - - -- Alfredo Deza Thu, 24 Mar 2016 10:53:47 +0000 - - ceph (10.0.5) stable; urgency=low - - * New upstream release (just fixing changelog) - - -- Sage Weil Fri, 11 Mar 2016 12:04:26 -0500 - - ceph (10.0.4) stable; urgency=low - - * New upstream release - - -- Sage Weil Thu, 03 Mar 2016 13:34:18 -0500 - - ceph (10.0.3) stable; urgency=low - - * New upstream release - - -- Sage Weil Mon, 08 Feb 2016 17:10:25 -0500 - - ceph (10.0.2-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Wed, 13 Jan 2016 16:22:26 +0000 - - ceph (10.0.1-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Mon, 14 Dec 2015 23:48:54 +0000 - - ceph (10.0.0-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Mon, 16 Nov 2015 21:41:53 +0000 - - ceph (9.2.0-1) stable; urgency=low - - * New upstream release - - -- Jenkins Build Slave User Tue, 03 Nov 2015 16:58:32 +0000 - - ceph (9.1.0-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Tue, 13 Oct 2015 05:56:36 -0700 - - ceph (9.0.3-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Fri, 21 Aug 2015 12:46:31 -0700 - - ceph (9.0.2-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Tue, 14 Jul 2015 13:10:31 -0700 - - ceph (9.0.1-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Fri, 05 Jun 2015 10:59:02 -0700 - - ceph (9.0.0-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Mon, 04 May 2015 12:32:58 -0700 - - ceph (0.94-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Tue, 07 Apr 2015 10:05:40 -0700 - - ceph (0.93-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Fri, 27 Feb 2015 09:52:53 -0800 - - ceph (0.92-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Mon, 02 Feb 2015 10:35:27 -0800 - - ceph (0.91-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Tue, 13 Jan 2015 12:10:22 -0800 - - ceph (0.90-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Fri, 19 Dec 2014 06:56:22 -0800 - - ceph (0.89-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Wed, 03 Dec 2014 08:18:33 -0800 - - ceph (0.88-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Tue, 11 Nov 2014 09:33:12 -0800 - - ceph (0.87-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Wed, 29 Oct 2014 11:03:55 -0700 - - ceph (0.86-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Tue, 07 Oct 2014 06:20:21 -0700 - - ceph (0.85-1) stable; urgency=low - - * Development release - - -- Alfredo Deza Mon, 08 Sep 2014 06:31:31 -0700 - - ceph (0.84-1) stable; urgency=low - - * Development release - - -- Alfredo Deza Mon, 18 Aug 2014 09:02:20 -0700 - - ceph (0.83-1) stable; urgency=low - - * Development release - - -- Alfredo Deza Tue, 29 Jul 2014 13:42:53 -0700 - - ceph (0.82-1) stable; urgency=low - - * Development release - - -- Alfredo Deza Wed, 25 Jun 2014 16:47:51 +0000 - - ceph (0.81-1) stable; urgency=low - - * Development release - - -- Alfredo Deza Mon, 02 Jun 2014 18:37:27 +0000 - - ceph (0.80-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Tue, 06 May 2014 14:03:27 +0000 - - ceph (0.80-rc1-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Tue, 22 Apr 2014 21:21:44 +0000 - - ceph (0.79-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Mon, 07 Apr 2014 16:48:36 +0000 - - ceph (0.78-1) stable; urgency=low - - * New upstream release - - -- Alfredo Deza Fri, 21 Mar 2014 22:05:12 +0000 - - ceph (0.77-1) stable; urgency=low - - * New upstream release - - -- Ken Dreyer Wed, 19 Feb 2014 22:54:06 +0000 - - ceph (0.76-1) stable; urgency=low - - * New upstream release - - -- Ken Dreyer Mon, 03 Feb 2014 18:14:59 +0000 - - ceph (0.75-1) stable; urgency=low - - * New upstream release - - -- Ken Dreyer Mon, 13 Jan 2014 21:05:07 +0000 - - ceph (0.74-1) stable; urgency=low - - * New upstream release - - -- Gary Lowell Mon, 30 Dec 2013 21:02:35 +0000 - - ceph (0.73-1) precise; urgency=low - - * New upstream release - - -- Gary Lowell Tue, 10 Dec 2013 04:55:06 +0000 - - ceph (0.72-1) stable; urgency=low - - * New upstream release - - -- Gary Lowell Thu, 07 Nov 2013 20:25:18 +0000 - - ceph (0.72-rc1-1) stable; urgency=low - - * New upstream release - - -- Gary Lowell Wed, 30 Oct 2013 00:44:25 +0000 - - ceph (0.71-1) stable; urgency=low - - * New upstream release - - -- Gary Lowell Thu, 17 Oct 2013 09:19:02 +0000 - - ceph (0.70-1) stable; urgency=low - - * New upstream release - - -- Gary Lowell Fri, 04 Oct 2013 20:11:51 +0000 - - ceph (0.69-1) precise; urgency=low - - * New upstream release - - -- Gary Lowell Wed, 18 Sep 2013 01:39:47 +0000 - - ceph (0.68-1) precise; urgency=low - - * New upstream release - - -- Gary Lowell Tue, 03 Sep 2013 16:10:11 -0700 - - ceph (0.67-1) precise; urgency=low - - * New upstream release - - -- Gary Lowell Tue, 13 Aug 2013 10:44:30 -0700 - - ceph (0.67-rc3-1) precise; urgency=low - - * New upstream release - - -- Gary Lowell Tue, 30 Jul 2013 14:37:40 -0700 - - ceph (0.67-rc2-1) precise; urgency=low - - * New upstream release - - -- Gary Lowell Wed, 24 Jul 2013 16:18:33 -0700 - - ceph (0.67-rc1-1) precise; urgency=low - - * New upstream release - - -- Gary Lowell Mon, 22 Jul 2013 11:57:01 -0700 - - ceph (0.66-1) precise; urgency=low - - * New upstream release - - -- Gary Lowell Mon, 08 Jul 2013 15:44:45 -0700 - - ceph (0.65-1) precise; urgency=low - - * New upstream release - - -- Gary Lowell Tue, 25 Jun 2013 09:19:14 -0700 - - ceph (0.64-1) precise; urgency=low - - * New upstream release - - -- Gary Lowell Wed, 12 Jun 2013 09:53:54 -0700 - - ceph (0.63-1) precise; urgency=low - - * New upstream release - - -- Gary Lowell Tue, 28 May 2013 13:57:53 -0700 - - ceph (0.62) precise; urgency=low - - * New upstream release - - -- Gary Lowell Tue, 14 May 2013 09:08:21 -0700 - - ceph (0.61-1) precise; urgency=low - - * New upstream release - - -- Gary Lowell Mon, 06 May 2013 13:18:43 -0700 - - ceph (0.60-1) precise; urgency=low - - * New upstream release - - -- Gary Lowell Mon, 01 Apr 2013 12:22:30 -0700 - - ceph (0.59-1) precise; urgency=low - - * New upstream release - - -- Gary Lowell Tue, 19 Mar 2013 22:26:37 -0700 - - ceph (0.58-1) precise; urgency=low - - * New upstream release - - -- Gary Lowell Mon, 04 Mar 2013 15:17:58 -0800 - - ceph (0.57-1) quantal; urgency=low - - * New upstream release - - -- Gary Lowell Tue, 19 Feb 2013 10:06:39 -0800 - - ceph (0.56-1) quantal; urgency=low - - * New upstream release - - -- Gary Lowell Mon, 31 Dec 2012 17:08:45 -0800 - - ceph (0.55.1-1) precise; urgency=low - - * New upstream release - - -- Gary Lowell Wed, 12 Dec 2012 16:24:13 -0800 - - ceph (0.55-1) precise; urgency=low - - * New upstream release - - -- Gary Lowell Mon, 03 Dec 2012 19:08:14 -0800 - - ceph (0.54-1) precise; urgency=low - - * New upstream release - - -- Gary Lowell Tue, 13 Nov 2012 13:17:19 -0800 - - ceph (0.53-1) precise; urgency=low - - * New upstream release - - -- Gary Lowell Tue, 16 Oct 2012 17:40:46 +0000 - - ceph (0.52-1) precise; urgency=low - - * New upstream release - - -- Ubuntu Thu, 27 Sep 2012 16:16:52 +0000 - - ceph (0.51-1) experimental; urgency=low - - * New upstream release - - -- Sage Weil Sat, 25 Aug 2012 15:58:23 -0700 - - ceph (0.50-1) experimental; urgency=low - - * New upstream release - - -- Sage Weil Mon, 13 Aug 2012 09:44:40 -0700 - - ceph (0.49-1) experimental; urgency=low - - * New upstream release - - -- Sage Weil Fri, 20 Jul 2012 23:26:43 -0700 - - ceph (0.48argonaut-1) experimental; urgency=low - - * New upstream release - - -- Sage Weil Sat, 30 Jun 2012 14:49:30 -0700 - - ceph (0.47.3-1) experimental; urgency=low - - * New upstream release - - -- Sage Weil Wed, 20 Jun 2012 10:57:03 -0700 - - ceph (0.47.2-1) experimental; urgency=low - - * New upstream release - - -- Sage Weil Wed, 23 May 2012 09:00:43 -0700 - - ceph (0.47.1-1) experimental; urgency=low - - * New upstream release - - -- Sage Weil Mon, 21 May 2012 14:28:30 -0700 - - ceph (0.47-1) experimental; urgency=low - - * New upstream release - - -- Sage Weil Sun, 20 May 2012 15:16:03 -0700 - - ceph (0.46-1) experimental; urgency=low - - * New upstream release - - -- Sage Weil Sun, 29 Apr 2012 21:21:01 -0700 ++ * Add allow-binding-on-lo.patch. ++ * New upstream release. + - ceph (0.45-1) experimental; urgency=low ++ -- Thomas Goirand Fri, 15 Jan 2021 12:26:14 +0100 + - * New upstream release ++ceph (14.2.15-4) unstable; urgency=medium + - -- Sage Weil Tue, 10 Apr 2012 10:41:57 -0700 ++ * Add upstream 3 patches for libboost 1.74 (Closes: #977243). + - ceph (0.44.2-1) experimental; urgency=low ++ -- Thomas Goirand Sun, 13 Dec 2020 16:33:57 +0100 + - * New upstream release ++ceph (14.2.15-3) unstable; urgency=medium + - -- Sage Weil Thu, 05 Apr 2012 14:54:17 -0700 ++ [ Adrian Bunk ] ++ * [197afaf] Merge branch 'debian/unstable' into 'debian/unstable' ++ Portability fixes ++ See merge request ceph-team/ceph!6 ++ ++ -- Thomas Goirand Thu, 03 Dec 2020 21:03:06 +0100 ++ ++ceph (14.2.15-2) unstable; urgency=medium ++ ++ * Do not build with clang, instead, set --max-parallel=1, as it seems it ++ worked for Ubuntu. If this doesn't work, we'll disable the non-64 bits ++ arch completely. ++ ++ -- Thomas Goirand Mon, 30 Nov 2020 21:10:12 +0100 ++ ++ceph (14.2.15-1) unstable; urgency=medium ++ ++ * New upstream release (Closes: #956750): ++ - Fix FTBFS with GCC-10 (Closes: #957079). ++ - Fix CVE-2020-10753 (Closes: #963760). ++ - Fix CVE-2020-25660 (Closes: #975275). ++ * Refreshed patches: ++ - 32bit-avoid-size_t.patch ++ - add-option-to-disable-ceph-dencoder.patch ++ - bluefs-use-uint64_t-for-len.patch ++ - disable-crypto.patch ++ - mds-purgequeue-use_uint64_t.patch ++ * Raw wrap-and-sort -bastk. ++ * Added myself as uploader. ++ * Added librdkafka-dev as build-depends. ++ * Fixed debian/libcephfs-dev.install. ++ * debian/calc-max-parallel.sh: allow for more values of --max-parallel so ++ that ceph builds faster on more powerful machines. ++ * Add a patch to make Ceph aware of Python 3.9: ++ - make-ceph-python-3.9-aware.patch ++ * Add a debian/source/options to ignore CRLF to CR changes. ++ * Use --home in ceph-common.postinst when creating the Ceph system user. ++ * Updated debian/libcephfs2.symbols (added 3 new symbols). ++ * Package: ceph-resource-agents, switch Priority: to optional. ++ * Add debian/source.lintian-overrides to allow .js which shipped by upstream ++ in both compiled and source version. ++ * Removed now useless dpkg-maintscript-helper rm_conffile: they have been ++ around for more than one release. ++ * debian/ceph-osd.postinst: remove as it's doing nothing. ++ * Fix debian/lib{rbd1,rados2}.symbols (3 missing symbols). ++ ++ -- Thomas Goirand Fri, 27 Nov 2020 23:58:00 +0100 + - ceph (0.44.1-1) experimental; urgency=low ++ceph (14.2.9-1) unstable; urgency=high ++ ++ * [dc4e7cf] Update upstream source from tag 'upstream/14.2.9' ++ Update to upstream version '14.2.9' ++ with Debian dir 544321a5823a0e5b826198888c79cb3ed4dd9b2e ++ Closing #956142 / CVE-2020-1760 ++ ++ -- Bernd Zeimetz Fri, 24 Apr 2020 22:43:18 +0200 ++ ++ceph (14.2.8-2) unstable; urgency=medium + - * New upstream release ++ * [eed9184] Fix 32bit issues in src/mds/PurgeQueue.cc ++ mips64el (as reported in the bug report) built fine. ++ s390x is a buildd issue, gets stuck sometimes for unknown (and not ++ reproducible) reasons. ++ Other build issues are fixed hopefully. ++ Thanks to Ivo De Decker (Closes: #953749) ++ ++ -- Bernd Zeimetz Mon, 23 Mar 2020 00:14:25 +0100 ++ ++ceph (14.2.8-1) unstable; urgency=medium ++ ++ * [e14a030] Update upstream source from tag 'upstream/14.2.8' ++ Update to upstream version '14.2.8' ++ with Debian dir 6c28e7789e84694b28409f0aceb9bfe6f2acdade ++ Closes: #953364 ++ * [6bc660e] Refreshing patches ++ * [9e935e3] Fix FTBFS on riscv64. ++ Thanks to Aurelien Jarno (Closes: #953003) ++ * [4287e84] fix lintian override + - -- Sage Weil Tue, 27 Mar 2012 13:02:00 -0700 ++ -- Bernd Zeimetz Sun, 08 Mar 2020 22:31:55 +0100 ++ ++ceph (14.2.7-1) unstable; urgency=medium ++ ++ * [a68d12f] Update upstream source from tag 'upstream/14.2.7' ++ Update to upstream version '14.2.7' ++ with Debian dir 1125b03b88e8da85cf70f7cc540c1c30fa95d456 ++ This also contains a fix for ++ [CVE-2020-1700]: Fixed a flaw in RGW beast frontend that ++ could lead to denial of service from an unauthenticated client. ++ CVE-2020-1699 was patched since 14.2.6-3. ++ * [1474595] Removing patches applied upstream ++ ++ -- Bernd Zeimetz Sat, 01 Feb 2020 00:47:27 +0100 ++ ++ceph (14.2.6-6) unstable; urgency=medium ++ ++ * [c18d632] Remove broken patch (Option::TYPE_SIZE as uint64_t). ++ This patch actually results in a segfault while parsing options. ++ Revert it and then see what it was actually needed for on 32bit. ++ Thanks to Martin Mlynář (Closes: #949743) ++ * [b5c7be8] Update patch with a non-segfaulting version. ++ Took me some time to figure out that in option.cc size_t is not what you ++ expect, but a struct instead, just with the same name. ++ So to make clang happy we'll use static_cast now, although ++ this will for sure show various other issues on 32bit as not all ++ possible config values will fit into 32bit numbers. ++ Fixing this will need a lot of upstream work unfortunately. ++ ++ -- Bernd Zeimetz Sun, 26 Jan 2020 15:39:29 +0100 ++ ++ceph (14.2.6-5) unstable; urgency=medium ++ ++ * [966df1a] Removing cython from Build-deps. ++ Thanks to Sandro Tosi (Closes: #936282) ++ * [38fdd89] clang ist not available on sh4 ++ * [3c97474] Replace findstring by filter where needed. ++ Thanks jrtc27 for the hint. ++ * [c694d0d] Pass -DHAVE_NEON=0 to cmake on armel. ++ Instead of "fixing" CMakeCache.txt. ++ * [825a942] Revert "Don't build ceph on mipsel." ++ This reverts commit 424ea9b82f956daa8fa9c0539d0752ccfdc7caf6. ++ Thanks to peter green (Closes: #949528) ++ * [79aef26] Remove merge fail ++ ++ -- Bernd Zeimetz Tue, 21 Jan 2020 21:21:17 +0100 ++ ++ceph (14.2.6-4) unstable; urgency=high ++ ++ * Really uploading to unstable now. ++ ++ -- Bernd Zeimetz Sat, 18 Jan 2020 19:58:32 +0100 ++ ++ceph (14.2.6-3) experimental; urgency=high ++ ++ * Uploading to unstable, including changes to make ceph ++ build on mipsel again (Closes: #948722). ++ * [1bac6f0] mgr/dashboard: fix improper URL checking. ++ This change disables up-level references beyond the HTTP base directory. ++ [CVE-2020-1699] ++ Upstream commit 0443e40c11280ba3b7efcba61522afa70c4f8158 ++ Thanks to Salvatore Bonaccorso (Closes: #949206) ++ * [720ce76] Updating changelog (from experimental) ++ ++ -- Bernd Zeimetz Sat, 18 Jan 2020 19:11:22 +0100 ++ ++ceph (14.2.6-2) experimental; urgency=medium ++ ++ * [fc4df2f] d/rules: make sure mips doesn't match on mipsel/mips64el ++ * [3d9ed86] Add patch to disable dencoder build if needed. ++ * [ce0c9bb] Don't build ceph-dencoder on mipsel. ++ Its a debugging tool and does not build on mipsel due to g++/clang++ ++ running out of memory. ++ Replacing it by a shellscript that prints an error message. ++ * [0d83e22] Configure gbp for experimental ++ ++ -- Bernd Zeimetz Thu, 16 Jan 2020 23:59:37 +0100 ++ ++ceph (14.2.6-1) unstable; urgency=medium ++ ++ [ James Page ] ++ * [2e50d5b] Fix misnamed package Recommends brbd1 -> librbd1 ++ * [11df8ed] Add missing debhelper misc:Depends for python3-ceph ++ * [08c3c8b] Add missing Depends on python3-{distutils,routes} to ceph-mgr-dashboard package ++ ++ [ Bernd Zeimetz ] ++ * [a87f434] Update upstream source from tag 'upstream/14.2.6' ++ Update to upstream version '14.2.6' ++ with Debian dir f37aa9f99ec09cc88d8e5e468c1f642fa7f77ef1 ++ * [e91626b] Revert "Fix ceph-mgr - indefinite queue growth hangs" ++ This reverts commit 010db9a30458a6417ff667c3c11a3870edb8ee0c. ++ Patches were applied upstream for 14.2.6 ++ ++ -- Bernd Zeimetz Sun, 12 Jan 2020 23:13:27 +0100 ++ ++ceph (14.2.5-3) unstable; urgency=medium ++ ++ * Uploading to unstable ++ ++ * [010db9a] Fix ceph-mgr - indefinite queue growth hangs. ++ Applying the backport for the fix ++ https://github.com/ceph/ceph/pull/32466 ++ Thanks to Milan Kupcevic (Closes: #947969) ++ * [b01de37] Merge branch 'debian/unstable' into debian/experimental ++ * [c8f35e5] Add breaks/replaces for ceph-common - ceph mds. ++ * [ee905cb] Revert "Configure gbp for experimental" ++ This reverts commit 3bcd5ac5f416b902a868036c243d7f19752c82f8. ++ * [6303513] Revert "CI: build in experimental" ++ This reverts commit d481122833e611c69c28e2b381e1cc1c8f689385. ++ * [f1a9482] Snapshot changelog ++ * [6e955c8] Removing automatic Ubuntu header ++ * [b90d95a] Mark patch as forwarded ++ ++ -- Bernd Zeimetz Tue, 07 Jan 2020 20:50:28 +0100 ++ ++ceph (14.2.5-2) experimental; urgency=medium ++ ++ * [8c74414] lower --max-parallel for >=16GB ++ g++ loves to eat ram ++ * [b15dcdd] Build-dep. on python3-dev instead python3-all-dev. ++ Thanks to Graham Inggs (Closes: #948021) ++ * [d481122] CI: build in experimental ++ * [4303a75] 32bit: fix more size_t vs uint64_t issues. ++ * [c98ea07] Install bash-completion in /usr again. ++ This change went missing somewhere during the import of the ++ changes done in Ubuntu between 12.2.11 and 14.2.4. ++ Thanks to Andreas Beckmann (Closes: #948165) ++ * [c7d90b9] Move manpages to ceph-common again. ++ This also went missing during the import. ++ * [3e5a680] Use a better way to check if we are on 32bit. ++ * [c03cd06] rm d/p/boost-py37-compat.patch. ++ Upstream renamed assert.h to ceph_assert.h, so this patch should not be ++ necessary anymore. ++ ++ -- Bernd Zeimetz Sun, 05 Jan 2020 00:04:23 +0100 ++ ++ceph (14.2.5-1) experimental; urgency=medium ++ ++ [ Bernd Zeimetz ] ++ * [3bcd5ac] Configure gbp for experimental ++ * [bd0b051] New upstream version 14.2.5 ++ * [46cbe61] Merge upstream changes for 14.2.5 ++ * [4dfd819] Refreshing patches ++ * [da26f25] Fix copy&paste errors in build-deps. ++ * [7ff43a2] Mark build-deps needed for make check. ++ And remove the need to install them. ++ * [5ef8ac3] Remove left over patch file ++ * [91ab5b9] */lib_tp.so files are not built, don't install them. ++ * [44591e4] Don't try to install files we don't build ++ * [db0994e] librbd1.symbols: add new symbols. ++ * [d53724e] Add install/postinstall files for ceph-mgr-k8sevents ++ * [acada37] Add lintian override for .chm file. ++ Source and build info is shipped. ++ * [bbb0bd6] copy the radosgw init file in override_dh_installinit. ++ * [a5958d5] Avoid duplicate files. ++ etc/bash_completion.d/ceph was accidentally shipped in ceph-base again. ++ * [fbc33a3] Add missing > in Dependency. ++ ++ -- Bernd Zeimetz Thu, 02 Jan 2020 10:52:50 +0100 ++ ++ceph (14.2.4-9) unstable; urgency=medium ++ ++ * [8c74414] lower --max-parallel for >=16GB ++ g++ loves to eat ram ++ * [b15dcdd] Build-dep. on python3-dev instead python3-all-dev. ++ Thanks to Graham Inggs (Closes: #948021) ++ * [c98ea07] Install bash-completion in /usr again. ++ This change went missing somewhere during the import of the ++ changes done in Ubuntu between 12.2.11 and 14.2.4. ++ Thanks to Andreas Beckmann (Closes: #948165) ++ * [c7d90b9] Move manpages to ceph-common again. ++ This also went missing during the import. ++ ++ -- Bernd Zeimetz Sun, 05 Jan 2020 00:22:21 +0100 ++ ++ceph (14.2.4-8) unstable; urgency=medium ++ ++ * [e187e6a] Use WITH_CCACHE from cmake to build with ccache. ++ * [8cbe25e] Hack CMakeCache.txt to disable HAVE_ARM_NEON on armel. ++ after trying to patch the various places where HAVE_ARM_NEON is used ++ the easiest way to get rid of it on armel seems to be to patch the cmake ++ cache file. ++ * [424ea9b] Don't build ceph on mipsel. ++ No compiler is able to build the code without running into oom-issues. ++ ++ -- Bernd Zeimetz Wed, 01 Jan 2020 19:29:48 +0100 ++ ++ceph (14.2.4-7) unstable; urgency=medium ++ ++ * [9b97753] Make sure we use ccache if needed. ++ * [3dbd1ac] d/rules: Remove the armel fpu options. ++ Hopefully properly patched now. ++ * [da253e4] m68k, sh4: build with clang to avoid gcc OOM. ++ ++ -- Bernd Zeimetz Tue, 31 Dec 2019 00:13:23 +0100 ++ ++ceph (14.2.4-6) unstable; urgency=medium ++ ++ * [b1c9b5d] Try to reduce memory usage even further if needed. ++ gcc loves to eat too much memory on armhf mipsel armel. ++ * [d695778] Remove softfp patch in favour of build flags. ++ This hopefully avoids the need to update and debug the patch. ++ * [6eddb32] Build with clang(++) on armhf mipsel armel. ++ Reports seem to suggest that clang does not need as much memory as gcc. ++ * [b9420ba] Fix unsigned/size_t issue for sh4 & m86k. ++ * [0027181] Updating changelog ++ * [6502f60] Fix another 32bit size_t/uint64_t issue. ++ In src/common/options.cc line 192. ++ * [4a0b044] Fix another 32bit size_t/uint64_t issue. ++ This time: powerpc. ++ ++ -- Bernd Zeimetz Sun, 29 Dec 2019 17:38:10 +0100 ++ ++ceph (14.2.4-5) unstable; urgency=medium ++ ++ [ Bernd Zeimetz ] ++ * [453eaa4] Avoid using make -j 32 on powerpc. ++ A lot of CPU do not always help. ++ ++ [ Milan Kupcevic ] ++ * [c6ec924] cherry pick critical bluestore data corruption fix ++ (Closes: #947457) ++ ++ [ Bernd Zeimetz ] ++ * [e88fc21] Set -DWITH_BOOST_CONTEXT=OFF where necessary. ++ [!s390x !mips64el !ia64 !m68k !ppc64 !riscv64 !sh4 !sparc64 !x32] ++ ++ -- Bernd Zeimetz Sat, 28 Dec 2019 15:54:51 +0100 ++ ++ceph (14.2.4-4) unstable; urgency=medium ++ ++ * [b70efb1] Create missing directories for arch:all build. ++ * [3e4530f] try to save even more memory on armhf. ++ Don't build debug flags at all. ++ * [b478ee5] Avoid overloading on mipsel. ++ Add mipsel to debian/patches/32bit-avoid-overloading.patch ++ * [85eb6e9] Also build jerasure with softfp on armel. ++ ++ -- Bernd Zeimetz Thu, 26 Dec 2019 16:03:51 +0100 ++ ++ceph (14.2.4-3) unstable; urgency=medium ++ ++ [ Bernd Zeimetz ] ++ * [f3f47f5] CI: disable extra-long running tests. ++ ++ [ Steve Langasek ] ++ * [9794fc4] Drop uninstallable and unneeded server binaries on i386. ++ (Closes: #947156) ++ ++ [ Bernd Zeimetz ] ++ * [6c2993f] Merge tag 'debian/14.2.4-0ubuntu3' into debian/unstable ++ * [0c5b41f] Use a tracker.d.o list instead of a closed one. (Closes: #760538) ++ * [d95db97] Try to build with --max-parallel=1 on slow arches. ++ We run into out-of-memory errors again. ++ * [e8d9e63] Use -mfloat-abi=softfp on armel for NEON instructions. ++ And again, that patch went missing somewhere. ++ Taken from ++ https://salsa.debian.org/ceph-team/ceph/commit/fa7d0d84f736d0b8450572f3192a43ff7b3252c4 ++ ++ -- Bernd Zeimetz Tue, 24 Dec 2019 13:03:45 +0100 ++ ++ceph (14.2.4-2) unstable; urgency=medium ++ ++ [ Thomas Goirand ] ++ * [4b2327d] Add a python3-ceph metapackage. ++ ++ [ Bernd Zeimetz ] ++ * [dbc7d2f] Add lintian override for empty python3-ceph package. ++ * [5381390] Remove -en from description. ++ We actually want to have a description in the changes file... ++ * [4a57f31] Make python3-ceph dependencies binNMU safe ++ ++ -- Bernd Zeimetz Thu, 28 Nov 2019 09:43:37 +0100 ++ ++ceph (14.2.4-1) unstable; urgency=medium ++ ++ * Uploading 14.2.4 to Debian. ++ (Closes: #936282, #943961, #940854, #942733) ++ * Adding missing sources (two.js, bootstrap 3.3.4) ++ * ceph-mon.postinst missed the interpreter ++ * Add missing dependency on python3 ++ * ignore lintian errors about minified js with shipped sources ++ * Radowgw uses a systemd template, override lintian. ++ * libcephfs-jni: rpath to java libraries needed. ++ Add lintian override. ++ * Remove .pc folder from debian folder. ++ * Adding myself to Uploaders ++ * Merging the work done in Ubuntu. ++ ++ [ Dariusz Gadomski ] ++ * d/p/issue37490.patch: Cherry pick fix to optimize LVM queries in ++ ceph-volume, resolving performance issues in systems under heavy load ++ or with large numbers of disks (LP: #1850754). ++ ++ [ James Page ] ++ * d/p/issue40114.patch: Cherry pick endian fixes to resolve issues ++ using Ceph on big-endian architectures such as s390x (LP: #1851290). ++ * New upstream release (LP: #1850901): ++ - d/p/more-py3-compat.patch,ceph-volume-wait-for-lvs.patch, ++ ceph-volume-wait-for-lvs.patch: Drop, included upstream. ++ - d/p/bluefs-use-uint64_t-for-len.patch: Cherry pick fix to resolve ++ FTBFS on 32 bit architectures. ++ * d/rules: Disable SPDK support as this generates a build which ++ has a minimum CPU baseline of 'corei7' on x86_64 which is not ++ compatible with older CPU's (LP: #1842020). ++ * d/p/issue40781.patch: Cherry pick fix for py3 compatibility in ceph- ++ crash. ++ ++ [ Eric Desrochers ] ++ * Ensure that daemons are not automatically restarted during package ++ upgrades (LP: #1840347): ++ - d/rules: Use "--no-restart-after-upgrade" and "--no-stop-on-upgrade" ++ instead of "--no-restart-on-upgrade". ++ - d/rules: Drop exclusion for ceph-[osd,mon,mds] for restarts. ++ ++ [ Jesse Williamson ] ++ * d/p/civetweb-755-1.8-somaxconn-configurable*.patch: Backport changes ++ to civetweb to allow tuning of SOMAXCONN in Ceph RADOS Gateway ++ deployments (LP: #1838109). ++ ++ [ James Page ] ++ * d/p/ceph-volume-wait-for-lvs.patch: Cherry pick inflight fix to ++ ensure that required wal and db devices are present before ++ activating OSD's (LP: #1828617). ++ ++ [ Steve Beattie ] ++ * SECURITY UPDATE: RADOS gateway remote denial of service ++ - d/p/CVE-2019-10222.patch: rgw: asio: check the remote endpoint ++ before processing requests. ++ - CVE-2019-10222 ++ - Closes: #936015 ++ ++ [ James Page ] ++ * New upstream release. ++ * d/p/fix-py3-encoding-fsid.patch: Drop, no longer required. ++ * d/p/pybind-auto-encode-decode-cstr.patch: Drop, reverted upstream. ++ * d/p/fix-py3-encoding-fsid.patch: Cherry pick correct fix to resolve ++ FSID encoding issues under Python 3 (LP: #1833079). ++ * d/p/pybind-auto-encode-decode-cstr.patch: Cherry pick fix to ensure ++ that encoding/decoding of strings is correctly performed under ++ Python 3 (LP: #1833079). ++ ++ * New upstream release. ++ * d/p/misc-32-bit-fixes.patch: Drop, included upstream. ++ * d/p/py37-compat.patch: Drop, included upstream. ++ * d/p/collections.abc-compat.patch: Drop, included in release. ++ * d/p/*: Refresh. ++ * d/*: Re-sync packaging with upstream for Nautilus release. ++ * d/control,ceph-test.*,rules: Disable build of test binaries, drop ++ ceph-test binary package (reduce build size). ++ * d/control,rules: Use system boost libraries (reduce build time). ++ * d/control: Add dependency on smartmontools, suggest use of nvme-cli ++ for ceph-osd package. ++ * d/p/32bit-*.patch: Fix misc 32 bit related issues which cause ++ compilation failures on armhf and i386 architectures. ++ * d/control: Add Breaks/Replaces on ceph-common for ceph-argparse to ++ deal with move of Python module. ++ ++ * New upstream release (LP: #1810766). ++ * d/p/*: Refresh. ++ ++ * d/p/more-py3-compat.patch: Add more py3 fixes. ++ ++ * d/p/more-py3-compat.patch: Misc Python 3 fixes in ceph-create-keys. ++ ++ * d/tests/python-ceph: Fix python3 test support resolving ++ autopkgtest failure. ++ ++ * New upstream point release. ++ * d/p/*: Refresh. ++ * d/control,python-*.install,rules: Drop Python 2 support. ++ * d/tests: Update for Python 2 removal. ++ * d/p/misc-32-bit-fixes.patch: Update type of rgw_max_attr_name_len, ++ resolving SIGABRT in radosgw (LP: #1805145). ++ * d/p/boost-py37-compat.patch: Fix compilation issue with boost ++ imports conflicting with ceph's assert.h header. ++ * d/p/collections.abc-compat.patch: Selective cherry-pick of upstream ++ fix for future compatibility with Python 3.8, avoiding deprecation ++ warnings under Python 3.7. ++ ++ * d/ceph-mds.install: Install missing systemd configuration ++ (LP: #1789927). ++ ++ * Re-instate 32bit architectures. ++ - d/control: Switch back to linux-any ++ - d/p/misc-32-bit-fixes.patch: Misc fixes for compilation ++ failures under 32 bit architectures. ++ - d/rules: Disable SPDK integration under i386. ++ * Repack upstream tarball, excluding non-DFSG sources (LP: #1750848): ++ - d/copyright: Purge upstream tarball of minified js files, which ++ are neither shipped in binaries or required for package build. ++ - d/watch: Add dversionmangle for +dfsg\d version suffix. ++ * d/control,rules: Drop requirement for gcc-7 for arm64. ++ * d/ceph-osd.udev: Add udev rules for sample LVM layout for OSD's, ++ ensuring that LV's have ceph:ceph ownership (LP: #1767087). ++ ++ * d/copyright,source.lintian-overrides: Exclude jsonchecker component ++ of rapidjson avoiding license-problem-json-evil non-free issue. ++ * New upstream point release. ++ * d/control: Remove obsolete X{S}-* fields. ++ ++ * New upstream release. ++ * Sync with changes in upstream packaging: ++ - d/*.install,rules: Use generated systemd unit files for install ++ - d/ceph-test.install: Drop binaries removed upstream. ++ * d/p/*: Refresh and drop as needed. ++ * d/*.symbols: Refresh for new release. ++ * d/rules,calc-max-parallel.sh: Automatically calculate the maximum ++ number of parallel compilation units based on total memory. ++ * d/control: Drop support for 32 bit architectures. ++ * d/control: Update Vcs-* fields for Ubuntu. ++ * d/control: Drop min python version field. ++ ++ -- Bernd Zeimetz Mon, 18 Nov 2019 14:18:10 +0100 ++ ++ceph (12.2.11+dfsg1-2.1) unstable; urgency=medium ++ ++ * Non-maintainer upload. ++ * [3194010] Install ceph-volume@.service into ceph-osd. ++ (Closes: #924061) ++ ++ -- Bernd Zeimetz Fri, 05 Apr 2019 15:12:52 +0200 ++ ++ceph (12.2.11+dfsg1-2) unstable; urgency=medium ++ ++ * [27a321] Fix builds on 32bit architectures ++ * [346bfa] Fix linking radosgw without BEAST frontend ++ ++ -- Gaudenz Steinlin Tue, 19 Feb 2019 08:50:12 +0100 ++ ++ceph (12.2.11+dfsg1-1) unstable; urgency=medium ++ ++ * [8b6f70] Build depend on cmake >= 3.13.2 ++ * [98ed84] New upstream version 12.2.11+dfsg1 ++ - Fixes CVE-2018-14662, CVE-2018-16889, CVE-2018-16846 ++ (Closes: #921948, #918969, #921947) ++ * [4d5c86] Mark all LTTng tracepoints symbols as optional ++ * [b92a2f] Build depend on debhelper >= 11.5.4~ ++ * [5781cc] Add Breaks/Replaces ceph-base (<< 12.2.10+dfsg1-1~) to ceph-common ++ (Closes: #919898) ++ * [658bd3] Fixup Breaks/Replaces for files moved between binary packages ++ * [c24137] Install systemd units for Ceph MGR service ++ (Closes: #920176, #919871) ++ * [091e14] Remove no longer needed dpkg-maintscript-helper calls in radosgw ++ * [567dde] Temp changelog commit ++ ++ -- Gaudenz Steinlin Tue, 12 Feb 2019 10:55:02 +0100 ++ ++ceph (12.2.10+dfsg1-1) unstable; urgency=medium ++ ++ * [22146e] ceph-base: create directory for bootstrap-rbd key ++ * [8c0362] Move ceph bash completion to ceph-common ++ * [b9e790] Make ceph binary package Suggests binNMU safe ++ * [fc83f1] Install ceph-fuse systemd service file ++ * [e70c29] Enable LTTng ++ * [234e9b] New upstream version 12.2.10+dfsg1 ++ ++ -- Gaudenz Steinlin Sat, 29 Dec 2018 22:08:52 +0100 ++ ++ceph (12.2.8+dfsg1-5) unstable; urgency=medium ++ ++ * [66e03b] Fix linking on archs which require libatomic ++ * [5236f3] Fix Python 3 autopkgtest ++ ++ -- Gaudenz Steinlin Fri, 30 Nov 2018 16:49:02 +0100 ++ ++ceph (12.2.8+dfsg1-4) unstable; urgency=medium ++ ++ * [97dfb6] Fix detection of armel for NEON instructions (Closes: #913599) ++ ++ -- Gaudenz Steinlin Thu, 22 Nov 2018 22:33:21 +0100 ++ ++ceph (12.2.8+dfsg1-3) unstable; urgency=medium ++ ++ * [168bb9] Build depend on pythonX-dev instead of pythonX-all-dev ++ (Closes: #912905) ++ * [38d140] Patch to check for atomic support during build (Closes: #913601) ++ * [9cc0a5] Disable libboost-context on unsupported archs (Closes: #913600) ++ * [238143] Fix building with -g1 on 32 bit architectures ++ * [3b48d5] Use -mfloat-abi=softfp on armel for NEON instructions ++ (Closes: #913599) ++ ++ -- Gaudenz Steinlin Thu, 22 Nov 2018 09:38:09 +0100 ++ ++ceph (12.2.8+dfsg1-2) unstable; urgency=medium ++ ++ * [5c4b36] Add architecture specific symbols for librados2 ++ * [38bc1b] Build depend on libatomic1 on armel, m68k, mips, mipsel, powerpc, ++ powerpcspe and sh4 ++ ++ -- Gaudenz Steinlin Wed, 31 Oct 2018 23:38:05 +0100 ++ ++ceph (12.2.8+dfsg1-1) unstable; urgency=medium ++ ++ [ James Page ] ++ * [9c55f6] Ensure that systemd targets are enabled and started ++ * [17ca38] Support optional runtime loading of openssl in radosgw ++ * [8927f6] Fix build on i386 ++ * [774281] Ensure all ceph modules are included in the binary package. ++ * [00ca38] Cherry pick upstream fix to resolve FTBFS on armhf ++ * [5d118c] Add ceph-volume tools to ceph-osd package ++ ++ [ Gaudenz Steinlin ] ++ * [ba768b] New upstream version 12.2.8+dfsg1 ++ (Closes: #852999, #864535, #893149) ++ * [96eba0] Exclude jsonchecker from upstream source ++ * [7dd73b] Exclude Windows help file from orig tarball ++ * [9d1740] Move ceph initscript and systemd target to ceph-base ++ * [8ae049] Drop obsolete patches (either upstreamed or because of switch to ++ cmake) ++ * [cfd095] Create directory for Ceph Manager bootstrap keys ++ * [858194] Shell scripts moved out of architecture specific directory ++ * [f3f387] Merge ceph-fs-common package into ceph-common ++ * [1789fe] Move radosgw-admin to ceph-common ++ * [6f51fc] Add compressor plugins to ceph-common ++ * [5bd0d1] Add crypto plugins to ceph-common on amd64 ++ * [7724f7] New binary package ceph-mgr ++ * [804be6] Remove librgw_file* from ceph-test. These are unit tests for rgw. ++ * [fef87a] Upstream changed the build system to cmake ++ * [134e34] Build Python 3 versions of Python modules (Closes: #883148) ++ * [52a969] Package RADOS gateway Python bindings (python-rgw) ++ * [a48700] Add rados-objclass-dev binary package ++ * [30306f] Update dependencies for new upstream release ++ * [4d07af] libcephfs1 -> libcephfs2 soname bumped upstream ++ * [c4022d] Remove ceph-disk-udev no longer shipped upstream ++ * [9aad82] Remove sample.fetch_config no longer shipped upstream ++ * [fc163d] Update copyright for new upstream release ++ * [21c408] Add /usr/bin/radosgw-es to radosgw package ++ * [f63b2a] ceph-osd: sysctl config to increase the maximum number of AIO ++ requests ++ * [5f1f30] Remove static libraries from -dev packages (removed upstream) ++ * [1db168] Install ceph-detect-init into /usr/bin instead of /usr/sbin ++ * [1fb625] Move ceph-*-tool from ceph-test into daemon packages ++ * [6a5628] Remove obsolete X-Python-Version ++ * [c6fc4d] Update to Debian Policy version 4.2.1 ++ * [5821f2] Set multiarch triplet in debian/rules ++ * [9bdce0] Remove override of dh_auto_install ++ * [b71c87] Add libceph-common to librados2 and librados-dev pkgs ++ * [c746ce] Override JSON license lintian warning (false positive, code ++ removed) ++ * [17d65c] Use dh_missing instead of dh_install --list-missing ++ * [cba40a] Remove obsolete ceph-create-keys@.service from ceph-base ++ * [252d62] Update watch file for https and repacking ++ * [ef84cf] Install ceph SysV init script with dh_install ++ * [0c2c84] Add missing source for jquery.flot.js (actually ++ jquery.colorhelpers.js) ++ * [0f1e2e] Add missing sources for JQuery in civetweb ++ * [8010cc] Add missing sources from AdminLTE ++ * [7c425a] Add lintian overrides for source-is-missing false positives ++ * [75f7d2] Update symbols files ++ * [aa7295] Patch to fix build failures with Boost 1.67 ++ * [872887] Build with Debian packaged Boost 1.67 instead of bundled Boost 1.66 ++ * [abf215] Use google-perftools on all supported architectures ++ * [44dd74] Add missing sources for the mgr dashboard plugin ++ * [39f9d6] Depend on ceph-common for pythonX-cephfs (Closes: #896400) ++ * [7a10f0] Improve autopkgtests ++ * [7515ca] Add Python dependency for ceph-fuse ++ * [d91a92] Make the ceph binary package a pure metapackage ++ * [df1dc8] Add Lintian overrides for systemd targets ++ * [bc7cd4] Install upstream rbdmap systemd service file ++ * [dd44ea] Remove RUNPATH from JNI libraries ++ * [7e9bce] Add dependency on junit4 and libcephfs-java for ceph-test ++ * [dfbe7c] Remove unnecessary ceph-base postrm script (Closes: #867465) ++ * [bad29d] Fix permissions on /var/run/ceph in SysV init script ++ (Closes: #869142) ++ * [6edc7a] Mark libraries as Multi-Arch compatible (Closes: #822740) ++ ++ [ Thomas Goirand ] ++ * [46be6f] Change VCS links to point to Salsa ++ ++ [ Shengjing Zhu ] ++ * [bfcf95] Don't treat rados-classes and ceph/compressor as shared libraries ++ * [0ef4f9] Add missing interpreter in ceph-{osd,mon}.postinst ++ (Closes: #862684, #862685) ++ * [f75bfc] Change section of libcephfs-jni from libs to java ++ * [f4b5d4] Add patch to fix various spelling errors ++ * [f60d21] Patch to remove link to ceph.com for dashboard favicon ++ * [116028] Backport test build fix from upstream ++ * [b83166] Change architecture of ceph-fuse to linux-any (from amd64) ++ ++ -- Gaudenz Steinlin Sun, 28 Oct 2018 23:43:10 +0100 ++ ++ceph (10.2.5-7.2) unstable; urgency=medium ++ ++ * Non-maintainer upload. ++ * Build with -g1 instead of -g on 32bit architectures to fix ++ FTBFS due to the 2GB/3GB address space limits. ++ ++ -- Adrian Bunk Wed, 07 Jun 2017 11:39:39 +0300 ++ ++ceph (10.2.5-7.1) unstable; urgency=medium ++ ++ * Non-maintainer upload. ++ * ceph-mon: Add Breaks+Replaces: ceph-common (<< 10) for taking over ++ /usr/bin/ceph-rest-api. (Closes: #864161) ++ ++ -- Andreas Beckmann Tue, 06 Jun 2017 09:08:30 +0200 ++ ++ceph (10.2.5-7) unstable; urgency=medium ++ ++ * [a12798] Add fix-init-system-detection.patch. ++ This replaces the static init system detection by more appropriate ++ runtime detection which also works if a Debian system is running with ++ sysvinit. (Closes: #862075) ++ ++ -- Gaudenz Steinlin Fri, 12 May 2017 12:12:00 +0200 ++ ++ceph (10.2.5-6) unstable; urgency=medium ++ ++ * [e44a30] Disable running dh_auto_install with parallel jobs ++ (Closes: #850906) ++ * [b7e926] Link with libatomic and --as-needed on all archs (Closes: #850831) ++ ++ -- Gaudenz Steinlin Wed, 11 Jan 2017 12:02:24 +0100 ++ ++ceph (10.2.5-5) unstable; urgency=medium ++ ++ * [f4675d] Set _FILE_OFFSET_BITS=64 via DEB_CPPFLAGS_MAINT_APPEND ++ * [f60392] Install systemd targets for ceph services (Closes: #850509) ++ * [d3baba] Link with -latomic on mips/mipsel ++ ++ -- Gaudenz Steinlin Tue, 10 Jan 2017 09:04:48 +0100 ++ ++ceph (10.2.5-4) unstable; urgency=medium ++ ++ * [88034c] Build with ggc-min-expand=5 on mips/el (Closes: #849657) ++ * [c31e79] Add patch to build rocksdb with -latomic on mips/el ++ * [182dd8] NEWS entry about upgrades from Debian Jessie ++ ++ -- Gaudenz Steinlin Sun, 08 Jan 2017 21:34:21 +0100 ++ ++ceph (10.2.5-3) unstable; urgency=medium ++ ++ * [eeff8d] Use -mfloat-abi=softfp on armel for NEON plugin (Closes: #849660) ++ ++ -- Gaudenz Steinlin Thu, 05 Jan 2017 09:18:41 +0100 ++ ++ceph (10.2.5-2) unstable; urgency=medium ++ ++ * [3859ca] Only list missing files instead of failing (Closes: #849031) ++ * [47aef7] Remove chrony from recommends (provides time-daemon) ++ (Closes: #827673) ++ * [3e96e6] Upstream fix for CVE-2016-9579 (short CORS request) ++ (Closes: #849048) ++ ++ -- Gaudenz Steinlin Sat, 24 Dec 2016 13:14:28 +0100 ++ ++ceph (10.2.5-1) unstable; urgency=medium ++ ++ [ James Page ] ++ * [754935] Imported Upstream version 9.2.0 ++ - [df85c3] Resync relevant packaging changes with upstream. ++ - [be5f82] Refresh patches. ++ - [d1f3fe] Add python-setuptools to BD's for ceph-detect-init. ++ - [b2f926] Add lsb-release to BD's to ensure that python modules are ++ installed to correct locations. ++ - [e4d702] Add python-sphinx to BD's to ensure man pages get generated ++ and installed. ++ - [3ead6e] Correct install location for ceph-monstore-update tool. ++ - [269754] [177b7a] Update symbols for new release. ++ * [4c45629] Update NEWS file for infernalis changes. ++ * [940491e] Limit number of parallel builds to 2 to reduce memory footprint ++ on builders. ++ * [a2bed4] New upstream version 10.2.5 ++ * [cbfb6b] Sync upstream changes around rbdmap install and conf files. ++ * [5c52b2] Drop patches include upstream, refresh remaining patches ++ * [8f9820] Resync further packaging changes from upstream. ++ * [27d010] Re-add bz2-dev BD. ++ * [e94aa2] Add python-dev to BD's. ++ * [e201c1] d/p/pybind-flags.patch: Ensure that cython *FLAGS are correctly ++ set. ++ * [14cd12] d/p/fix-systemd-escaping.patch: Ensure that leading '/' is stripped ++ from block device paths when escaping for use in systemd unit ++ names. ++ * [33b3aa] Add pull request for systemd fixes ++ * [d781a2] d/ceph-mds.postinst: Fix syntax error. ++ * [ce55ec] Ensure that python flags are correct set for cython rbd build. ++ * [f9e35b] Switch rbd python binding to cython ++ * [8ccae0] Drop ceph_volume_client until its actually in the codebase. ++ * [0a11cc] Install to relative, not absolute /etc/ceph. ++ * [e2415a] Drop ceph-bluefs-tool from ceph package. ++ * [b16966] d/ceph-mds.dirs: Actually create /var/lib/ceph/mds prior to ++ changing permissions (LP: #1544647). ++ * [0c1201] d/ceph.init: Restore link to init-ceph, resolving un-install ++ failures due to missing init script (LP: #1546112). ++ ++ [ Gaudenz Steinlin ] ++ * [e3cb86] Remove no longer needed check for dh-autoreconf>=6 ++ * [9157b3] Install ceph-brag tool into ceph-common ++ * [74e9f3] Install ceph-client-debug into ceph-test ++ * [476449] Use jh_installlib to install Java libraries ++ * [643f6e] Don't remove configure script on dh_clean ++ * [4bb203] Remove *.la files to binary packages ++ * [b76c6a] Add build dependency on libboost-iostreams-dev ++ * [b0506f] Add build dependency on libldap2-dev ++ * [526ce1] Refresh patches for upstream version 10.2.2 ++ * [cf539b] Remove patches applied upstream in 10.2.2 ++ ++ [ James Page ] ++ * [646b48] Add librgw2 binary packages. ++ * [ae3cda] Drop virtualenv from BD's. ++ * [7ebd7c] Re-enable rocksdb build for bluestore support ++ * [e393e5] Add rbd-mirror package ++ * [00d06d] Add patch to set g++ flags correctly for rocksdb ++ * [3b029a] Enable gperftools on arm64 architecture. ++ * [e88620] Add ceph-bluefs-tool to install. ++ * [4f40a1] Add s390x to list of rocksdb flags ++ * [858334] d/p/skip-setup.py-makefiles.patch,rules: Avoid use of virtualenv to ++ install ceph-disk and ceph-detect-init python modules. ++ * [40c3b7] Update watch file to scan for gz files. ++ * [49b716] Install librgw_file* as part of ceph-test package. ++ ++ [ Gaudenz Steinlin ] ++ * [2e156d] d/rules: Install upstart and systemd configurations for rbd-mirror. ++ ++ [ James Page ] ++ * [ca0b07] d/copyright: Ensure that jerasure and gf-complete are not stripped ++ from the upstream release tarball. ++ * [eee861] d/p/disable-openssl-linking.patch: Disable build time linking with ++ OpenSSL due to licensing incompatibilities. ++ * [07d7a6] d/*.symbols: Add new symbols for RC. ++ * [2416f1] Fix multiarch paths for librgw ++ * [2da01d] d/rules: Ensure that dh_systemd_start does not insert maintainer ++ script snippets for ceph-mon and ceph-create-keys - service restart ++ should be handled outside of the packaging as it is under upstart ++ and for all other systemd unit files installed (LP: #1563330). ++ * [76ec3b] d/ceph-common.postinst: Silence output of usermod call ++ (LP: #1569249). ++ * [63d60f] d/rules,ceph-common.install: Ensure that /etc/default/ceph is a ++ file and not a directory (LP: #1587516). ++ * [a866ca] d/control: Bumped Standards-Version to 3.9.8, no changes. ++ * [e0811e] d/ceph.install: Drop install of 60-ceph-partuuid-workaround.rules ++ as no longer part of upstream codebase. ++ * [25954f] * d/*: Split ceph-osd and ceph-mon into separate binary packages ++ and add new ceph-base binary package inline with upstream packaging ++ changes (LP: #1596063). ++ * [f1287b] Add missing misc depends ++ * [afeb18] d/rules,control: Drop -dbg packages and let Ubuntu builds take care ++ of ddeb generation instead. ++ ++ [ Gaudenz Steinlin ] ++ * [e133b2] Install rbdmap manpage into ceph-common ++ * [b5ed64] Install radosgw-token command into radosgw package ++ * [0a27e1] Adapt installation of Python files to latest Jewel release ++ * [ef2544] Drop rocksdb-flags patch applied upstream ++ * [07aef4] Add patch osd-limit-omap-data-in-push-op ++ * [32f14d] Refresh patches ++ ++ [ James Page ] ++ * [f4e95c] Fix install location for mount.ceph ++ * [6a9efc] Update install location for mount.ceph.fuse ++ * [7624f0] d/control: Add Pre-Depends on ceph-common to ceph-osd to ensure ++ that ceph user and group are created prior to unpacking of udev ++ rules (LP: #1631328). ++ ++ [ Frode Nordahl ] ++ * [022ee3] Add rgw_rados-creation_time patch ++ ++ [ Gaudenz Steinlin ] ++ * [05225f] Remove old ceph logrotate configuration ++ ++ [ James Page ] ++ * [cfa82f] Refresh patches ++ ++ [ Gaudenz Steinlin ] ++ * [fdec5d] Remove upstart support ++ * [5966f0] Use new RSA key for ceph-post-file ++ * [3748b6] Only install ChangeLog as upstream changelog ++ * [aa1393] Install bash-completions to /usr ++ * [ab0a88] Dependency on lsb-base for initscripts ++ * [4d78b0] Disable radosgw SysV init script on systemd ++ * [ff4c87] Don't install python bytecode ++ * [3cbf5e] Add missing dependencies on python (ceph-osd, ceph-mon) ++ * [8c70df] Update symbols files (librados2 and librbd1) ++ * [e4f0c5] Add ${shlibs:Depends} on Python C extensions ++ * [936838] Allow setting the number of parallel jobs ++ * [e5aa0f] Remove Laszlo and add myself to uploaders ++ ++ -- Gaudenz Steinlin Sat, 17 Dec 2016 22:40:22 +0100 ++ ++ceph (0.94.5-1) experimental; urgency=medium ++ ++ * [2d330d6] New upstream release: ++ - [1e93090] Drop patch for CVE-2015-5245, included upstream. ++ - [20adc7d] Refresh all other patches. ++ * [9255e5d] Ensure any erasure coding test libraries and dangling symlinks ++ are not included in the ceph package. ++ ++ -- James Page Mon, 09 Nov 2015 12:09:51 +0000 ++ ++ceph (0.94.3-1) experimental; urgency=medium ++ ++ * [580fef] Imported Upstream version 0.94.3 (Closes: #777814, #795178) ++ * [536935] Add upstream patch to fix CVE-2015-5245 (Closes: #798567) ++ ++ -- Gaudenz Steinlin Fri, 18 Sep 2015 16:55:23 +0200 ++ ++ceph (0.94.2-2) experimental; urgency=medium ++ ++ * Revert "Drop virtualenv BD, disable unit tests." ++ * Restore patches for test enablement. ++ * Display test-suite log output in the event of failures. ++ ++ -- James Page Mon, 20 Jul 2015 13:37:06 +0100 ++ ++ceph (0.94.2-1) experimental; urgency=medium ++ ++ * Resync with Ubuntu, introducing Ceph Hammer stable release: ++ - d/*.symbols: Update inline with upstream additions, use regex ++ for ceph version symbol. ++ - d/lib-systemd/system/ceph-create-keys.service: Automatically create ++ admin and bootstrap keys after ceph mon startup. ++ - d/p/vivid-does-systemd.patch: Ensure that disks prepared on vivid ++ or later use systemd for init. ++ - d/lib-systemd/system/*.service: Align nofile limits and restart config ++ with equivalent upstart configurations. ++ - d/p/fix-cycles-arch.patch: Skip initialization of cycles_per_sec ++ if rtdsc (or equivalent) is not supported. ++ - d/ceph{-common}.install,control: Move ceph_argparse.py down into ++ ceph-common package to fixup ceph cli usage/autopkgtest failure. ++ - d/control,ceph-common.install,librbd1.install: Move rbdnamer and ++ associated udev rules into ceph-common package. ++ - d/control,python-*: Split out rbd, rados and cephfs bindings into ++ separate python packages, move some bits into ceph/ceph-common. ++ - d/control: Move python-flask dependency to ceph package, only required ++ for REST API. ++ - d/control: Use google-perftools on arm64. ++ - d/control: Re-order Recommends to prefer ntp over chrony for Ubuntu. ++ - d/p/ceph-osd-prestart-path.patch: Fixup path for ceph-osd upstart ++ configuration pre-start script. ++ - d/p/fix-argparse-defaults.patch: Workaround behavioural change in ++ argparse set_defaults in python 2.7.9 ++ * New upstream point release: ++ - d/p/*: Refresh. ++ * d/p/use_system_jerasure.patch,d/control: Drop use of libjerasure ++ as the patch is intrusive and expensive to maintain; will revisit if ++ adopted upstream. ++ ++ -- James Page Tue, 16 Jun 2015 11:31:05 +0100 ++ ++ceph (0.87-2) experimental; urgency=low ++ ++ * Team upload. ++ ++ [ Gaudenz Steinlin ] ++ * README.Debian: added clarification about setting the hashpspool flag. ++ (Closes: #769596). ++ ++ [ James Page ] ++ * Added new "modules.patch" to mark new erasure coding libraries as ++ modules, wildcard install. ++ ++ [ Dmitry Smirnov ] ++ * Recommends: added "ntp" to list of time-daemon alternatives ++ (Closes: #767511). ++ * Introduced native systemd services (except "rbdmap"), (Closes: #769593). ++ * ceph-test: install forgotten files. ++ * Run post-build tests: ++ + updated "virtualenv-never-download.patch" to pass ++ "--system-site-packages" to virtualenv to prevent downloads. ++ + added new patches to disable network-dependent and failing tests. ++ * Patchworks: ++ - bug-9341.patch ++ + bug-10036.patch (to show OSD affinity in "ceph osd tree"). ++ Thanks, Mykola Golub. ++ + bug-10059.patch ++ + 0latest-giant.patch (Last-Update: 2014-11-15). ++ + sleep-recover.patch ++ + tests-disable.patch (to disable tests that need cluster). ++ + tests-disable-ceph-disk.patch ++ + use_system_gtest.patch (commented) ++ as first attempt to build with system "libgtest-dev". ++ + use_system_jerasure.patch ++ * Build-Depends: ++ + libjerasure-dev (>= 2.0.0-2~) ++ + virtualenv ++ + valgrind [amd64 armhf i386 powerpc] ++ * rules: pass "--without-lttng" to explicitly disable "lttng" to avoid ++ auto-enable if found. ++ * rules: disabled bundled RocksDB: ++ RocksDB suppose to improve performance of keyvaluestore OSDs but the ++ latter slow down to nearly unusable state when filled over 1 TiB even with ++ RocksDB. Moreover KV backend is experimental and super dangerous -- I lost ++ cluster due to OSD poisoning caused by KV OSD which was plugged only ++ during limited time. LevelDB is good enough, for now I see no reason to ++ use RocksDB especially considering that it is not packaged separately. ++ * Removed myself from Uploaders. ++ ++ -- Dmitry Smirnov Wed, 01 Apr 2015 11:47:38 +1100 ++ ++ceph (0.87-1) experimental; urgency=medium ++ ++ * New major upstream release [October 2014]. ++ + new "libradosstriper*" binary packages. ++ * Patchworks (removed old patches, refreshed remaining ones). ++ + "bug-9814.patch" to prevent OSD crash. Thanks, Haomai Wang. ++ * Install systemd sleep handler. ++ * Exclude erasure-code plugins from `dh_makeshlibs` processing to avoid ++ useless calls to `ldconfig` in maintainer scripts. ++ * Build-Depends: ++ + libbabeltrace-dev ++ + libbabeltrace-ctf-dev ++ + libbz2-dev ++ + libudev-dev ++ + zlib1g-dev ++ * Build with "--with-babeltrace". ++ * Build and statically link bundled RocksDB. ++ ++ -- Dmitry Smirnov Thu, 30 Oct 2014 12:43:49 +1100 ++ ++ceph (0.80.9-2) unstable; urgency=medium ++ ++ * [70fc1d] Add NEWS entry about CRUSH issues fixed in 0.80.9 ++ * [f41bb6] Add NEWS entry about rbd backed filesystems and systemd ++ ++ -- Gaudenz Steinlin Tue, 05 May 2015 21:29:15 +0200 ++ ++ceph (0.80.9-1) unstable; urgency=medium ++ ++ * [4b4e] Imported Upstream version 0.80.9 ++ * [7102] Remove patches firefly-latest and p2139 applied upstream ++ * [5869] Add myself to uploaders ++ ++ -- Gaudenz Steinlin Mon, 04 May 2015 08:49:37 +0200 ++ ++ceph (0.80.7-2) unstable; urgency=medium ++ ++ * Team upload. ++ * Build-Depends: +libjerasure-dev (>= 2.0.0-2~) ++ * New patch to use system "jerasure" library instead of its bundled copy. ++ * Removed myself from Uploaders. + - ceph (0.44-1) experimental; urgency=low ++ -- Dmitry Smirnov Thu, 11 Dec 2014 12:55:38 +1100 + - * New upstream release ++ceph (0.80.7-1) unstable; urgency=medium + - -- Sage Weil Sun, 18 Mar 2012 12:03:38 -0700 ++ * New upstream release [October 2014]. ++ * Minor update to long description of "rbd-fuse" (Closes: #765462). + - ceph (0.43-1) experimental; urgency=low ++ -- Dmitry Smirnov Thu, 16 Oct 2014 04:36:23 +1100 + - * New upstream release ++ceph (0.80.6-1) unstable; urgency=medium ++ ++ * New upstream release [October 2014]. ++ * Standards-Version: 3.9.6. ++ ++ -- Dmitry Smirnov Thu, 02 Oct 2014 23:07:04 +1000 ++ ++ceph (0.80.5-2) unstable; urgency=low ++ ++ * Patchworks: ++ + new patch for Ceph#9341 to dramatically (e.g seconds instead of ++ hours) reduce rejoin (i.e. MDS restart) time (fuse clients). ++ + new "p2139.patch". ++ + new patch with fixes from Firefly HEAD; ++ includes patch to fix FTBFS on alpha (Closes: #756892). ++ updated "librbd1.symbols"; ++ * Build-Depends: mark "yasm" as [amd64] (Closes: #760383). ++ * Recommends: + "time-daemon | chrony". ++ ++ -- Dmitry Smirnov Tue, 16 Sep 2014 03:54:15 +1000 + - -- Sage Weil Fri, 02 Mar 2012 08:53:10 -0800 ++ceph (0.80.5-1) unstable; urgency=medium + - ceph (0.42.2-1) experimental; urgency=low ++ * New upstream stable release: ++ - d/p/firefly-post-release.patch: Dropped, no longer required. ++ - d/lib{rados2,cephfs1}.symbols: Update with new symbols. + - * New upstream release ++ -- James Page Wed, 30 Jul 2014 10:15:40 +0100 + - -- Sage Weil Fri, 24 Feb 2012 12:59:38 -0800 ++ceph (0.80.4-1) unstable; urgency=medium + - ceph (0.42.1-1) experimental; urgency=low ++ * New upstream release [July 2014]. ++ * New patches: ++ + rbdmap1-mount.patch ++ + rbdmap2-hooks.patch ++ + rbdmap3-lazyumount.patch ++ + bug-8821.patch ++ * radosgw: removed unused lintian overrides. + - * New upstream release ++ -- Dmitry Smirnov Fri, 18 Jul 2014 02:33:39 +1000 + - -- Sage Weil Thu, 23 Feb 2012 18:46:23 -0800 ++ceph (0.80.1-2) unstable; urgency=low + - ceph (0.42-1) experimental; urgency=low ++ * Megapatch from "firefly" branch with post-0.80.1 fixes. ++ * Patches for upstream bugs 8342, 8624 and some cherry-picks. ++ * New "bash-completion.patch" with Bash completion improvements. ++ * New patch to fix FTBFS on 'hppa' (Closes: #748571). ++ * "sample.ceph.conf.patch": minor update. + - * New upstream release ++ -- Dmitry Smirnov Sat, 05 Jul 2014 20:29:44 +1000 + - -- Sage Weil Sun, 19 Feb 2012 15:30:20 -0800 ++ceph (0.80.1-1) unstable; urgency=low + - ceph (0.41-1) experimental; urgency=low ++ * New upstream release [May 2014]. ++ * Dropped all backported patches. ++ * New "sleep-recover" and "client-sleep[1,2,3]" patches to fix ++ fuse-client hang after resume from suspend [#8291]; thanks, Zheng Yan. ++ * New "gcj_search_path.patch" to find "jni.h" with gcj-jdk v4.9.0. + - * New upstream release ++ -- Dmitry Smirnov Wed, 14 May 2014 09:24:15 +1000 + - -- Sage Weil Fri, 27 Jan 2012 10:42:11 -0800 ++ceph (0.80-1) unstable; urgency=low + - ceph (0.40-1) experimental; urgency=low ++ * New upstream release [May 2014]. ++ + upload to unstable. ++ * Updated "README.Debian". ++ * Updated "debian/copyright"; Thanks, László Böszörményi. ++ * Added backported patches: ++ [8113, 8175, 8282, 8291, bp0001, sample.ceph.conf]. ++ * "gbp.conf": don't merge to experimental. ++ * lintian-overrides: spelling-error-in-binary * tEH the. + - * New upstream release ++ -- Dmitry Smirnov Wed, 07 May 2014 16:43:07 +1000 + - -- Sage Weil Fri, 13 Jan 2012 08:36:02 -0800 ++ceph (0.80~rc1-1) experimental; urgency=low + - ceph (0.39-1) experimental; urgency=low ++ * New upstream pre-release. ++ * Minor re-factoring of udev rules installation. ++ * ceph-common: added ceph-crush-location.1 man page. ++ * ceph-test-dbg: fixed Depends. + - * New upstream release ++ -- Dmitry Smirnov Thu, 24 Apr 2014 02:52:12 +1000 + - -- Sage Weil Fri, 02 Dec 2011 09:01:20 -0800 ++ceph (0.79-3) experimental; urgency=low + - ceph (0.38-1) experimental; urgency=low ++ * New "arch.patch" to detect build architecture using dpkg-architecture. ++ * Mark amd64-only symbols as such. ++ * Minor rules cleanup. + - * New upstream release ++ -- Dmitry Smirnov Sat, 19 Apr 2014 15:56:37 +1000 + - -- Sage Weil Thu, 10 Nov 2011 15:06:44 -0800 ++ceph (0.79-2) experimental; urgency=low + - ceph (0.37-1) experimental; urgency=low ++ [ James Page ] ++ * d/p/modules.patch,d/ceph.install: Mark all jerasure plugins as modules ++ and ensure they are all installed. + - * New upstream release ++ [ Dmitry Smirnov ] ++ * Patchworks: ++ - removed unused "defaults-leveldb-osd.patch". ++ + improved description of "modules.patch". Thanks, James Page. ++ + added new backported patches [#5469, #8008, _1606, spelling]. ++ * Added .symbols and "dh_makeshlibs -V" shlibs tightening (Closes: #744382). ++ * README.Debian: added note regarding kernel client mount option. ++ * copyright: added license for man files. ++ * control: "Suggests: logrotate". + - -- Sage Weil Mon, 17 Oct 2011 08:35:42 -0700 ++ -- Dmitry Smirnov Fri, 18 Apr 2014 18:27:01 +1000 + - ceph (0.36-1) experimental; urgency=low ++ceph (0.79-1) experimental; urgency=low + - * New upstream release ++ * New upstream release [April 2014]. ++ * Tighten dependency on ceph-common. ++ * Install pm-suspend handler to stop/start ceph services on suspend/resume. ++ * New (inactive) patch to bump OSD's leveldb defaults. ++ * Patches dropped (applied-upstream): ++ - init.patch ++ - logrotate.patch ++ - fix-defaultweight.patch ++ * Refreshed "modules.patch". + - -- Sage Weil Fri, 30 Sep 2011 09:29:29 -0700 ++ -- Dmitry Smirnov Tue, 08 Apr 2014 16:52:04 +1000 + - ceph (0.35-1) experimental; urgency=low ++ceph (0.78-2) experimental; urgency=low + - * New upstream release ++ * Standards to 3.9.5. ++ * debian/copyright: reviewed and updated. ++ * ceph-test: added lintian-override for "binary-without-manpage". ++ * Patchworks: ++ + refreshed/renamed/reordered "virtualenv-never-download.patch". ++ + new "init.patch" for init.d scripts lintianisation. ++ + new "logrotate.patch" to avoid rotating empty logs. ++ + new "fix-defaultweight.patch" to fix weight calculation on OSD start. ++ + new "gcj.patch" with partial fix to FTBFS with gcj-jdk. ++ * Use symlinks to simplify installation of init.d and logrotate scripts. ++ * Added retrospective changelog entry to mention new B-D "libblkid-dev". ++ * Added "debian/clean file". ++ * Added "README.Debian" file with some hopefully useful notes. ++ * Added "mount.fuse.ceph.8" man page. ++ * rules: ++ + "dh --with" optimised. ++ + set JAVAC to prevent FTBFS due to incorrect use of 'gcj', when detected. ++ + verbose mode for 'cp' and 'rm' commands. ++ + build with "--as-needed" to minimise needless linking. ++ * control: ++ + lintian/duplicate-short-description + consistent capitalisation. ++ + removed needless versioned dependencies from Build-Depends. ++ + added myself to Uploaders. + - -- Sage Weil Wed, 21 Sep 2011 09:36:03 -0700 ++ -- Dmitry Smirnov Tue, 25 Mar 2014 07:17:40 +1100 + - ceph (0.34-1) experimental; urgency=low ++ceph (0.78-1) experimental; urgency=medium ++ ++ * New upstream release: ++ - d/control: Add "xfslib-dev" and "libblkid-dev" to BD's. ++ - d/*: Sync relevant packaging changes from upstream. ++ - d/p/*: Drop upstreamed patches. ++ - d/p/modules.patch: Mark libcls_user.so and libec_jerasure.so as modules. ++ - d/ceph.install: Only install libec_jerasure.so. ++ * d/ceph-test.install: Install test binaries to /usr/lib/ceph/bin; they ++ really don't need to be installed on the default path. ++ * d/{ceph|radosgw|ceph-mds}.lintian-overrides: Add overrides for intentional ++ difference in naming and structure between upstart configurations and ++ init.d scripts. ++ ++ -- James Page Sat, 22 Mar 2014 18:27:40 +0000 ++ ++ceph (0.72.2-3) unstable; urgency=medium ++ ++ * Team upload. + - * New upstream release ++ [ James Page ] ++ * d/ceph-test.install: Install test binaries to /usr/lib/ceph/bin; they ++ really don't need to be installed on the default path. ++ ++ [ Dmitry Smirnov ] ++ * Tightened shlibs with "dh_makeshlibs -V" (Closes: #679686). ++ ++ -- Dmitry Smirnov Mon, 14 Apr 2014 17:28:20 +1000 ++ ++ceph (0.72.2-2) unstable; urgency=medium ++ ++ * d/radosgw.{postinst,postrm,preinst}: Handle renaming of radosgw ++ upstart configuration on upgrade@0.72.1-3. ++ * d/{ceph|ceph-mds|radosgw}.{postinst|prerm}: Check to ensure that system ++ is running upstart before trying to start/stop upstart configurations ++ (Closes: #734241, #738845, #738845). ++ ++ -- James Page Sat, 08 Mar 2014 16:48:28 +0000 ++ ++ceph (0.72.2-1) unstable; urgency=medium + - -- Sage Weil Fri, 26 Aug 2011 21:48:35 -0700 ++ * New upstream release. + - ceph (0.33-1) experimental; urgency=low ++ -- James Page Wed, 01 Jan 2014 09:32:03 +0000 ++ ++ceph (0.72.1-3) unstable; urgency=low ++ ++ * d/rules,ceph.install: Correct install paths for ceph-* helpers. ++ * d/p/modules: Mark libcls_kvs.so as module. ++ * d/rules: Rename radosgw upstart configuration to radosgw-instance to ++ avoid namespace conflict with init script which breaks backwards ++ compatibility (LP: #1255464). ++ ++ -- James Page Wed, 27 Nov 2013 10:52:48 +0000 ++ ++ceph (0.72.1-2) unstable; urgency=low ++ ++ * Fix upgrade failures from ceph < 0.67.3-1 (Closes: #728164): ++ - d/control: ceph-mds Breaks/Replaces ceph (<< 0.67.3-1). ++ - d/control: ceph-fs-common Breaks/Replaces ceph-common (<< 0.67.3-1). ++ * d/rules,control: Use google-perftools on armhf and powerpc archs. ++ ++ -- James Page Mon, 25 Nov 2013 10:13:19 +0000 ++ ++ceph (0.72.1-1) unstable; urgency=low ++ ++ * New upstream stable release: ++ - d/ceph-test.install: Add new ceph_filestore_tool, ceph-kvstore-tool ++ and ceph_test_cls_hello binaries, drop ceph_test_store_tool. ++ - d/ceph-common.install: Add new ceph-post-file binary and manpage. ++ - d/ceph.install: Tweaked install path /usr/sbin -> /sbin. ++ - d/control: Add new BD's on python-nose and yasm. ++ - d/copyright: Updates inline with changes in codebase. ++ - d/ceph.install,rules: Install rbdmap init file using dh_installinit. ++ - Refresh patches. ++ * d/control,rules: Disable unit testing; it requires a forked version of ++ cram and is still trying to download dependencies using virtualenv. ++ ++ -- James Page Fri, 22 Nov 2013 13:02:29 +0000 ++ ++ceph (0.67.3-1) unstable; urgency=low ++ ++ [ Laszlo Boszormenyi ] ++ * New upstream release (Closes: #693866, #705262). ++ * Update debian/copyright. ++ * Sync with Ubuntu. ++ ++ [ James Page ] ++ * d/control,rules,libcephfs-{java,jni}: Enable Java CephFS library, ++ add new BD's on javahelper and default-jdk, add dbg package. ++ * d/control: Add new BD on libboost-thread-dev for RADOS Gateway ++ keystone integration. ++ * d/{control,obsync.install}: Drop obsync package inline with ++ upstream. ++ * d/librbd-dev.install: Pickup new features.h file. ++ * Remove manual calls to ldconfig: ++ - d/lib{rados2|rbd1|cephfs1}.post*: Dropped - all these do is call ++ ldconfig which will automatically be done. ++ - d/rules: Let dh_makeshlibs do its magic with postinst/postrm. ++ * d/tests/*: Added autopkgtests for librbd, librados, python-ceph ++ and the ceph CLI. ++ * d/control: Fix versions of librbd1, librados2 and libcephfs1 for ++ python-ceph as it requires an exact version match. ++ * d/ceph.docs: Drop - README from upstream is only useful for developers ++ (Closes: #722957). ++ * d/rules: Drop --upstart-only from dh_installinit calls for upstart ++ configurations; this is deprecated in Ubuntu and not support in Debian. ++ * d/rules: Exclude jni package from shlibs generation to avoid pointless ++ ldconfig calls in maintainer scripts. ++ ++ [ Bastian Blank ] ++ * Use debhelper 9. ++ * Use dh-autoreconf. ++ * Install files from source tree if possible. ++ * Run test-suite: ++ - Build-depend on python-virtualenv. ++ - Ask virtualenv to never download anything. ++ * Fix clean target. ++ * Properly mark library modules: ++ - Don't longer exclude them from stripping. ++ * Drop all libtool .la files. ++ * Generate python dependencies. ++ * Don't exclude stuff from shlibs generation. ++ ++ -- Laszlo Boszormenyi (GCS) Tue, 01 Oct 2013 02:29:08 +0200 ++ ++ceph (0.48-1) unstable; urgency=low ++ ++ * New upstream release, the first with long-term support. ++ * As gceph dropped by upstream, remove it from packaging. ++ * Build with hardening enabled and build-conflict with libcryptopp not to ++ mix up with libnss. ++ * Use symbol versioning (closes: #679686). ++ * Update debian/watch to GitHub tags. ++ ++ -- Laszlo Boszormenyi (GCS) Sat, 07 Jul 2012 07:53:40 +0200 ++ ++ceph (0.47.2-1) unstable; urgency=low + - * New upstream release. ++ * New upstream release. ++ * Use system leveldb (closes: #667907). ++ * Remove librgw1 , librgw-dev and librgw1-dbg and add rest-bench and ++ rest-bench-dbg packages. ++ * Backport leveldb build fixes from upstream git as ++ fix_leveldb_dep_for_system_library_case.patch and ++ fix_leveldb_includes_for_system_library_case.patch . ++ * Update packaging. ++ * Sync with Ubuntu: switch build-dependency from libcryptopp to libnss as ++ libcryptopp is not seeded. + - -- Sage Weil Mon, 15 Aug 2011 16:42:07 -0700 ++ -- Laszlo Boszormenyi (GCS) Sun, 03 Jun 2012 13:37:52 +0200 + - ceph (0.32-1) experimental; urgency=low ++ceph (0.44.1-1) unstable; urgency=low + - * New upstream release ++ * New upstream release. + - -- Sage Weil Fri, 29 Jul 2011 21:42:08 -0700 ++ -- Laszlo Boszormenyi (GCS) Fri, 06 Apr 2012 01:10:15 +0200 + - ceph (0.30-1) experimental; urgency=low ++ceph (0.43-1) unstable; urgency=low + - * New upstream release ++ * New upstream release, now creates /var/run/ceph on each start ++ (closes: #660238). ++ * Update debian/copyright . + - -- Sage Weil Mon, 27 Jun 2011 20:06:06 -0700 ++ -- Laszlo Boszormenyi (GCS) Sun, 26 Feb 2012 04:07:02 +0100 + - ceph (0.29.1-1) experimental; urgency=low ++ceph (0.41-1) unstable; urgency=low + - * New upstream release ++ * New upstream release. + - -- Sage Weil Thu, 16 Jun 2011 13:10:47 -0700 ++ -- Laszlo Boszormenyi (GCS) Sun, 05 Feb 2012 10:07:38 +0100 + - ceph (0.29-1) experimental; urgency=low ++ceph (0.40-1) unstable; urgency=low + - * New upstream release ++ * New upstream release (closes: #652037). ++ * Adjust copyright to match upstream source changes. + - -- Sage Weil Mon, 06 Jun 2011 09:59:25 -0700 ++ -- Laszlo Boszormenyi (GCS) Sat, 14 Jan 2012 12:01:30 +0100 + - ceph (0.28.2-1) experimental; urgency=low ++ceph (0.38-1) unstable; urgency=low + - * New upstream release. ++ * New upstream release (closes: #647764), missingok is now part of logrotate ++ directives (closes: #645651). ++ * Rename ceph-client-tools package to ceph-common , libceph-dev to ++ libcephfs-dev and libceph1{,-dbg} ones to libcephfs1{,-dbg} respectively. ++ * Update upstream VCS locations. + - -- Sage Weil Sat, 28 May 2011 09:14:17 -0700 ++ -- Laszlo Boszormenyi (GCS) Sun, 27 Nov 2011 21:40:52 +0100 + - ceph (0.28.1-1) experimental; urgency=low ++ceph (0.35-1) unstable; urgency=low + - * New upstream release. ++ * New upstream release. + - -- Sage Weil Mon, 23 May 2011 21:11:30 -0700 ++ -- Laszlo Boszormenyi (GCS) Sat, 24 Sep 2011 16:51:57 +0200 + - ceph (0.28-1) experimental; urgency=low ++ceph (0.34-1) unstable; urgency=low + - * New upstream release. ++ * New upstream release (closes: #638714). ++ * Make librbd-dev depends on librados-dev as it uses headers from the latter ++ (closes: #636845). ++ * Add new binary packages, gceph, gceph-dbg and obsync . The libcrush ones ++ removed. ++ * Change to quilt source format and tune packaging. + - -- Sage Weil Tue, 17 May 2011 18:03:11 -0700 ++ -- Laszlo Boszormenyi (GCS) Sun, 28 Aug 2011 15:56:16 +0200 + - ceph (0.27.1-1) experimental; urgency=low ++ceph (0.27-1.1) unstable; urgency=low + - * New upstream release. ++ * Non-maintainer upload. ++ * Remove references to other libraries from dependency_libs field ++ (closes: #621208). + - -- Sage Weil Thu, 05 May 2011 13:42:06 -0700 ++ -- Luk Claes Sat, 28 May 2011 22:28:48 +0200 + - ceph (0.27-1) experimental; urgency=low ++ceph (0.27-1) unstable; urgency=low + - * New upstream release. ++ * New upstream release. + - -- Sage Weil Fri, 22 Apr 2011 16:51:49 -0700 ++ -- Laszlo Boszormenyi (GCS) Mon, 25 Apr 2011 10:09:05 +0200 + - ceph (0.26-1) experimental; urgency=low ++ceph (0.25.2-1) unstable; urgency=low + + * New upstream release. - * Make Ceph Linux only and build on all Linux archs (closes: #614890), - but only build-depend google-perftools on x86 and x64 archs only. - * Correct section of libcrush1, librados1, librbd1 and libceph1 to libs. + * Make Ceph cross buildable (closes: #618939), thanks to Hector Oron. + * Disable libatomic-ops on ARMv4t (armel) archs to prevent FTBFS + (closes: #615235), thanks go to Hector Oron again. + * Rename librados1{,-dbg,-dev} packages to librados2{,-dbg,-dev} ones; + conflict with and replace the former ones. ++ * Add librbd1 and librbd-dev packages. + - -- Laszlo Boszormenyi (GCS) Fri, 01 Apr 2011 16:28:11 +0100 - - ceph (0.25.2-1) experimental; urgency=low - - * New upstream release - - -- Sage Weil Sun, 20 Mar 2011 21:07:38 -0700 - - ceph (0.25.1-1) experimental; urgency=low - - * New upstream release - - -- Sage Weil Mon, 14 Mar 2011 14:43:47 -0700 ++ -- Laszlo Boszormenyi (GCS) Sun, 27 Mar 2011 15:51:23 +0200 + - ceph (0.25-1) experimental; urgency=low ++ceph (0.24.3-2) unstable; urgency=low + - * New upstream release ++ * Make Ceph Linux only and build on all Linux archs (closes: #614890). ++ * Support parallel building via DEB_BUILD_OPTIONS . ++ * Add watch file, thanks to Clint Byrum (closes: #615021). ++ * Tune packaging. + - -- Sage Weil Fri, 04 Mar 2011 14:39:54 -0800 ++ -- Laszlo Boszormenyi (GCS) Fri, 25 Feb 2011 15:17:26 +0100 + - ceph (0.24.3-1) experimental; urgency=low ++ceph (0.24.3-1) unstable; urgency=low + - * New upstream release ++ * New upstream bugfix release. + - -- Sage Weil Thu, 10 Feb 2011 09:14:00 -0800 ++ -- Laszlo Boszormenyi (GCS) Sat, 19 Feb 2011 12:25:43 +0100 + - ceph (0.24.2-1) experimental; urgency=low ++ceph (0.24.2-1) unstable; urgency=low + - * New upstream release. ++ * New upstream bugfix release. + - -- Sage Weil Mon, 24 Jan 2011 11:02:24 -0800 ++ -- Laszlo Boszormenyi (GCS) Sat, 29 Jan 2011 15:25:14 +0100 + - ceph (0.24.1-1) experimental; urgency=low ++ceph (0.24.1-1) unstable; urgency=low + - * New upstream release. ++ * New upstream bugfix release. + - -- Sage Weil Fri, 07 Jan 2011 16:49:48 -0800 ++ -- Laszlo Boszormenyi (GCS) Tue, 11 Jan 2011 22:23:18 +0100 + - ceph (0.24-1) experimental; urgency=low ++ceph (0.24-1) unstable; urgency=low + + * New upstream release. + + -- Laszlo Boszormenyi (GCS) Wed, 01 Dec 2010 09:26:25 -0800 + +ceph (0.23.1-1) experimental; urgency=low + + * Initial release (Closes: #506040) + + -- Sage Weil Sun, 21 Nov 2010 15:22:21 -0800 diff --cc debian/clean index 000000000,000000000..8638ac91a new file mode 100644 --- /dev/null +++ b/debian/clean @@@ -1,0 -1,0 +1,32 @@@ ++configure ++src/rocksdb/util/build_version.cc ++src/pybind/*.pyc ++src/test/pybind/*.pyc ++src/rapidjson/thirdparty/gtest/googlemock/msvc/2005/gmock.sln ++src/rapidjson/thirdparty/gtest/googlemock/msvc/2005/gmock.vcproj ++src/rapidjson/thirdparty/gtest/googlemock/msvc/2005/gmock_config.vsprops ++src/rapidjson/thirdparty/gtest/googlemock/msvc/2005/gmock_main.vcproj ++src/rapidjson/thirdparty/gtest/googlemock/msvc/2005/gmock_test.vcproj ++src/rapidjson/thirdparty/gtest/googlemock/msvc/2010/gmock.sln ++src/rapidjson/thirdparty/gtest/googlemock/msvc/2010/gmock.vcxproj ++src/rapidjson/thirdparty/gtest/googlemock/msvc/2010/gmock_config.props ++src/rapidjson/thirdparty/gtest/googlemock/msvc/2010/gmock_main.vcxproj ++src/rapidjson/thirdparty/gtest/googlemock/msvc/2010/gmock_test.vcxproj ++src/rapidjson/thirdparty/gtest/googletest/codegear/gtest.cbproj ++src/rapidjson/thirdparty/gtest/googletest/codegear/gtest.groupproj ++src/rapidjson/thirdparty/gtest/googletest/codegear/gtest_all.cc ++src/rapidjson/thirdparty/gtest/googletest/codegear/gtest_link.cc ++src/rapidjson/thirdparty/gtest/googletest/codegear/gtest_main.cbproj ++src/rapidjson/thirdparty/gtest/googletest/codegear/gtest_unittest.cbproj ++src/rapidjson/thirdparty/gtest/googletest/msvc/gtest-md.sln ++src/rapidjson/thirdparty/gtest/googletest/msvc/gtest-md.vcproj ++src/rapidjson/thirdparty/gtest/googletest/msvc/gtest.sln ++src/rapidjson/thirdparty/gtest/googletest/msvc/gtest.vcproj ++src/rapidjson/thirdparty/gtest/googletest/msvc/gtest_main-md.vcproj ++src/rapidjson/thirdparty/gtest/googletest/msvc/gtest_main.vcproj ++src/rapidjson/thirdparty/gtest/googletest/msvc/gtest_prod_test-md.vcproj ++src/rapidjson/thirdparty/gtest/googletest/msvc/gtest_prod_test.vcproj ++src/rapidjson/thirdparty/gtest/googletest/msvc/gtest_unittest-md.vcproj ++src/rapidjson/thirdparty/gtest/googletest/msvc/gtest_unittest.vcproj ++debian/ceph-common.logrotate ++debian/radosgw.init diff --cc debian/compat index ec635144f,000000000..f599e28b8 mode 100644,000000..100644 --- a/debian/compat +++ b/debian/compat @@@ -1,1 -1,0 +1,1 @@@ - 9 ++10 diff --cc debian/control index fdcfee29c,000000000..ea89565e4 mode 100644,000000..100644 --- a/debian/control +++ b/debian/control @@@ -1,1227 -1,0 +1,836 @@@ +Source: ceph +Section: admin +Priority: optional ++Maintainer: Ceph Packaging Team ++Uploaders: ++ James Page , ++ Gaudenz Steinlin , ++ Bernd Zeimetz , ++ Thomas Goirand , ++Build-Depends: ++ cmake, ++ cython3, ++ debhelper (>= 10~), ++ default-jdk, ++ dh-exec, ++ dh-python, ++ dpkg-dev (>= 1.16.1~), ++ gperf, ++ javahelper, ++ junit4, ++ libaio-dev, ++ libbabeltrace-ctf-dev, ++ libbabeltrace-dev, ++ libblkid-dev (>= 2.17), ++ libboost-atomic-dev (>= 1.67.0), ++ libboost-chrono-dev (>= 1.67.0), ++ libboost-context-dev (>= 1.67.0) [!s390x !mips64el !ia64 !m68k !ppc64 !riscv64 !sh4 !sparc64 !x32 !alpha], ++ libboost-coroutine-dev (>= 1.67.0) [!s390x !mips64el !ia64 !m68k !ppc64 !riscv64 !sh4 !sparc64 !x32 !alpha], ++ libboost-date-time-dev (>= 1.67.0), ++ libboost-iostreams-dev (>= 1.67.0), ++ libboost-program-options-dev (>= 1.67.0), ++ libboost-python-dev (>= 1.67.0), ++ libboost-random-dev (>= 1.67.0), ++ libboost-regex-dev (>= 1.67.0), ++ libboost-system-dev (>= 1.67.0), ++ libboost-thread-dev (>= 1.67.0), ++ libbz2-dev, ++ libcap-ng-dev, ++ libcunit1-dev, ++ libcurl4-gnutls-dev, ++ libedit-dev, ++ libexpat1-dev, ++ libfuse-dev, ++ libgoogle-perftools-dev [i386 amd64 powerpc armhf arm64 ppc64el], ++ libibverbs-dev, ++ libkeyutils-dev, ++ libldap2-dev, ++ libleveldb-dev, ++ liblz4-dev (>= 0.0~r131), ++ libncurses-dev, ++ libnl-3-dev, ++ libnl-genl-3-dev, ++ libnss3-dev, ++ liboath-dev, ++ librabbitmq-dev, ++ librdkafka-dev, ++ librdmacm-dev, ++ libsnappy-dev, ++ libssl-dev, ++ libtool, ++ libudev-dev, ++ libxml2-dev, ++ lsb-release, ++ pkg-config, ++ python3-cherrypy3, ++ python3-dev, ++ python3-pecan, ++ python3-setuptools, ++ python3-sphinx, ++ tox, ++ uuid-runtime, ++ valgrind [amd64 armhf i386 powerpc], ++ virtualenv, ++ xfslibs-dev, ++ yasm [amd64], ++ zlib1g-dev, ++Build-Conflicts: ++ libcrypto++-dev, ++Standards-Version: 4.2.1 ++Vcs-Git: https://salsa.debian.org/ceph-team/ceph.git ++Vcs-Browser: https://salsa.debian.org/ceph-team/ceph +Homepage: http://ceph.com/ - Vcs-Git: git://github.com/ceph/ceph.git - Vcs-Browser: https://github.com/ceph/ceph - Maintainer: Ceph Maintainers - Uploaders: Ken Dreyer , - Alfredo Deza , - Build-Depends: cmake (>= 3.5), - cpio, - cryptsetup-bin | cryptsetup, - cython, - cython3, - debhelper (>= 9), - default-jdk, - dh-exec, - dh-python, - dh-systemd, - git, - gperf, - javahelper, - # Make-Check jq, - junit4, - libaio-dev, - libbabeltrace-ctf-dev, - libbabeltrace-dev, - libblkid-dev (>= 2.17), - libcap-ng-dev, - libcunit1-dev, - libcurl4-openssl-dev, - libexpat1-dev, - libfuse-dev, - libgoogle-perftools-dev [i386 amd64 arm64], - libibverbs-dev, - librdmacm-dev, - libkeyutils-dev, - libldap2-dev, - libleveldb-dev, - liblttng-ust-dev, - liblz4-dev (>= 0.0~r131), - libncurses-dev, - libnss3-dev, - liboath-dev, - libsnappy-dev, - libssl-dev, - libtool, - libudev-dev, - libnl-3-dev, - libnl-genl-3-dev, - libxml2-dev, - librabbitmq-dev, - librdkafka-dev, - # Make-Check libxmlsec1 - # Make-Check libxmlsec1-nss - # Make-Check libxmlsec1-openssl - # Make-Check libxmlsec1-dev - lsb-release, - parted, - patch, - pkg-config, - python (>= 2.7), - python-all-dev, - python-cherrypy3, - # Make-Check python-jwt, - # Make-Check python-nose, - # Make-Check python-pecan, - # Make-Check python-bcrypt, - # Make-Check python-six, - # Make-Check python-tox, - # Make-Check python-coverage, - # Make-Check python-openssl, - # Make-Check python-prettytable, - # Make-Check python-requests, - python-setuptools, - python-sphinx, - # Make-Check python-werkzeug, - python3-all-dev, - python3-setuptools, - # Make-Check socat, - # Make-Check uuid-dev, - uuid-runtime, - valgrind, - virtualenv | python-virtualenv, - xfslibs-dev, - # Make-Check xfsprogs, - # Make-Check xmlstarlet, - yasm [amd64], - zlib1g-dev, - Standards-Version: 3.9.3 + +Package: ceph +Architecture: linux-any - Depends: ceph-mgr (= ${binary:Version}), - ceph-mon (= ${binary:Version}), - ceph-osd (= ${binary:Version}), - Recommends: ceph-mds (= ${binary:Version}), ++Depends: ++ ceph-mgr (= ${binary:Version}), ++ ceph-mon (= ${binary:Version}), ++ ceph-osd (= ${binary:Version}), ++ ${misc:Depends}, ++Suggests: ++ ceph-mds (= ${binary:Version}), +Description: distributed storage and file system + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + +Package: ceph-base +Architecture: linux-any - Depends: binutils, - ceph-common (= ${binary:Version}), - cryptsetup-bin | cryptsetup, - debianutils, - e2fsprogs, - findutils, - grep, - logrotate, - parted, - psmisc, - xfsprogs, - ${misc:Depends}, - ${python:Depends}, - ${shlibs:Depends}, - Recommends: btrfs-tools, - ceph-mds (= ${binary:Version}), - librados2 (= ${binary:Version}), - libradosstriper1 (= ${binary:Version}), - librbd1 (= ${binary:Version}), - ntp | time-daemon, - Replaces: ceph (<< 10), - ceph-common (<< 0.78-500), - ceph-test (<< 12.2.2-14), - python-ceph (<< 0.92-1223), - Breaks: ceph (<< 10), - ceph-test (<< 12.2.2-14), - python-ceph (<< 0.92-1223), ++Depends: ++ binutils, ++ ceph-common (= ${binary:Version}), ++ cryptsetup-bin | cryptsetup, ++ gdisk, ++ hdparm | sdparm, ++ parted, ++ uuid-runtime, ++ xfsprogs, ++ ${misc:Depends}, ++ ${python3:Depends}, ++ ${shlibs:Depends}, ++Pre-Depends: ++ ${misc:Pre-Depends}, ++Breaks: ++ ceph (<< 10.2.2-0ubuntu2~), ++ ceph-common (<< 9.2.0-0~), ++ ceph-test (<< 12.2.8+dfsg1-1~), ++ python-ceph (<< 0.94.1-1~), ++Replaces: ++ ceph (<< 12.2.8+dfsg1-1~), ++ ceph-common (<< 9.2.0-0~), ++ ceph-test (<< 12.2.8+dfsg1-1~), ++ python-ceph (<< 0.94.1-1~), ++Recommends: ++ ceph-mds (= ${binary:Version}), ++ chrony | time-daemon | ntp, ++ librados2 (= ${binary:Version}), ++ librbd1 (= ${binary:Version}), ++Suggests: ++ btrfs-tools, ++ logrotate, +Description: common ceph daemon libraries and management tools - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. ++ Ceph is a distributed storage system designed to provide excellent ++ performance, reliability, and scalability. + . + This package contains the libraries and management tools that are common among + the Ceph server daemons (ceph-mon, ceph-mgr, ceph-osd, ceph-mds). These tools + are necessary for creating, running, and administering a Ceph storage cluster. + - Package: ceph-base-dbg ++Package: ceph-common +Architecture: linux-any - Section: debug - Priority: extra - Depends: ceph-base (= ${binary:Version}), - ${misc:Depends}, - Description: debugging symbols for ceph-base - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. - . - This package contains the libraries and management tools that are common among - the Ceph server daemons (ceph-mon, ceph-mgr, ceph-osd, ceph-mds). These tools - are necessary for creating, running, and administering a Ceph storage cluster. ++Depends: ++ librbd1 (= ${binary:Version}), ++ python3-cephfs (= ${binary:Version}), ++ python3-prettytable, ++ python3-rados (= ${binary:Version}), ++ python3-rbd (= ${binary:Version}), ++ python3-requests, ++ ${misc:Depends}, ++ ${python3:Depends}, ++ ${shlibs:Depends}, ++Conflicts: ++ ceph-client-tools, ++Breaks: ++ ceph (<< 9.2.0-0~), ++ ceph-base (<< 12.2.10+dfsg1-1~), ++ ceph-fs-common (<< 12.2.10+dfsg1-1~), ++ ceph-mds (<< 14.2.5-3~), ++ ceph-test (<< 9.2.0-0~), ++ librbd1 (<< 0.94.1-1~), ++ python-ceph (<< 0.94.1-1~), ++ radosgw (<< 12.0.3-0~), ++Replaces: ++ ceph (<< 9.2.0-0~), ++ ceph-client-tools, ++ ceph-fs-common (<< 12.2.8+dfsg1-1~), ++ ceph-mds (<< 14.2.5-3~), ++ ceph-test (<< 9.2.0-1~), ++ librbd1 (<< 0.94.1-1~), ++ python-ceph (<< 0.94.1-1~), ++ radosgw (<< 12.0.3-0~), ++Suggests: ++ ceph, ++ ceph-mds, ++Description: common utilities to mount and interact with a ceph storage cluster ++ Ceph is a distributed storage and file system designed to provide ++ excellent performance, reliability, and scalability. This is a collection ++ of common tools that allow one to interact with and administer a Ceph cluster. ++ ++Package: ceph-fuse ++Architecture: amd64 ++Depends: ++ python3, ++ ${misc:Depends}, ++ ${python3:Depends}, ++ ${shlibs:Depends}, ++Recommends: ++ fuse, ++Description: FUSE-based client for the Ceph distributed file system ++ Ceph is a distributed network file system designed to provide ++ excellent performance, reliability, and scalability. This is a ++ FUSE-based client that allows one to mount a Ceph file system without ++ root privileges. + . - This package contains the debugging symbols for ceph-base. ++ Because the FUSE-based client has certain inherent performance ++ limitations, it is recommended that the native Linux kernel client ++ be used if possible. If it is not practical to load a kernel module ++ (insufficient privileges, older kernel, etc.), then the FUSE client will ++ do. + +Package: ceph-mds +Architecture: linux-any - Depends: ceph-base (= ${binary:Version}), - ${misc:Depends}, - ${shlibs:Depends}, - Recommends: ceph-fuse (= ${binary:Version}), - libcephfs2 (= ${binary:Version}), - Replaces: ceph (<< 0.93-417), - Breaks: ceph (<< 0.93-417), ++Depends: ++ ceph, ++ ${misc:Depends}, ++ ${shlibs:Depends}, ++Recommends: ++ ceph-common, ++ ceph-fuse, ++ libcephfs2, ++Breaks: ++ ceph (<< 0.67.3-1), ++Replaces: ++ ceph (<< 0.67.3-1), +Description: metadata server for the ceph distributed file system - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. ++ Ceph is a distributed storage and network file system designed to ++ provide excellent performance, reliability, and scalability. + . + This package contains the metadata server daemon, which is used to + create a distributed file system on top of the ceph storage cluster. + - Package: ceph-mds-dbg - Architecture: linux-any - Section: debug - Priority: extra - Depends: ceph-mds (= ${binary:Version}), - ${misc:Depends}, - Description: debugging symbols for ceph-mds - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. - . - This package contains the debugging symbols for ceph-mds. - +Package: ceph-mgr +Architecture: linux-any - Depends: ceph-base (= ${binary:Version}), - python-bcrypt, - python-cherrypy3, - python-enum34, - python-jwt, - python-openssl, - python-pecan, - python-requests, - python-werkzeug, - ${misc:Depends}, - ${python:Depends}, - ${shlibs:Depends}, - Recommends: ceph-mgr-dashboard, - ceph-mgr-diskprediction-local, - ceph-mgr-diskprediction-cloud, - ceph-mgr-rook, - ceph-mgr-k8sevents, - ceph-mgr-ssh - Suggests: python-influxdb - Replaces: ceph (<< 0.93-417), - Breaks: ceph (<< 0.93-417), - Description: manager for the ceph distributed storage system ++Depends: ++ ceph-base (= ${binary:Version}), ++ python3-bcrypt, ++ python3-cherrypy3, ++ python3-jwt, ++ python3-openssl, ++ python3-pecan, ++ python3-werkzeug, ++ ${misc:Depends}, ++ ${python3:Depends}, ++ ${shlibs:Depends}, ++Replaces: ++ ceph (<< 0.93-417), ++Breaks: ++ ceph (<< 0.93-417), ++Suggests: ++ ceph-mgr-dashboard, ++ ceph-mgr-diskprediction-cloud, ++ ceph-mgr-diskprediction-local, ++ ceph-mgr-rook, ++ ceph-mgr-ssh, ++Description: manager for the ceph distributed file system + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains the manager daemon, which is used to expose high + level management and monitoring functionality. + +Package: ceph-mgr-dashboard +Architecture: all - Depends: ceph-mgr (= ${binary:Version}), - python-cherrypy3, - python-jwt, - python-openssl, - python-bcrypt, - python-werkzeug, - python-routes, - ${misc:Depends}, - ${python:Depends}, - ${shlibs:Depends}, ++Depends: ++ ceph-mgr (>= ${binary:Version}), ++ python3-bcrypt, ++ python3-cherrypy3, ++ python3-distutils, ++ python3-jwt, ++ python3-openssl, ++ python3-routes, ++ python3-werkzeug, ++ ${misc:Depends}, ++ ${python3:Depends}, ++ ${shlibs:Depends}, +Description: dashboard plugin for ceph-mgr + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package provides a ceph-mgr plugin, providing a web-based + application to monitor and manage many aspects of a Ceph cluster and + related components. + . + See the Dashboard documentation at http://docs.ceph.com/ for details + and a detailed feature overview. + - Package: ceph-mgr-diskprediction-local - Architecture: all - Depends: ceph-mgr (= ${binary:Version}), - python-numpy, - python-scipy, - python-sklearn, - ${misc:Depends}, - ${python:Depends}, - ${shlibs:Depends}, - Description: diskprediction-local plugin for ceph-mgr - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. - . - This package contains the diskprediction_local plugin for the ceph-mgr - daemon, which helps predict disk failures. - +Package: ceph-mgr-diskprediction-cloud +Architecture: all - Depends: ceph-mgr (= ${binary:Version}), - ${misc:Depends}, - ${python:Depends}, - ${shlibs:Depends}, ++Depends: ++ ceph-mgr (>= ${binary:Version}), ++ ${misc:Depends}, ++ ${python3:Depends}, ++ ${shlibs:Depends}, +Description: diskprediction-cloud plugin for ceph-mgr + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains the diskprediction_cloud plugin for the ceph-mgr + daemon, which helps predict disk failures. + - Package: ceph-mgr-rook ++Package: ceph-mgr-diskprediction-local +Architecture: all - Depends: ceph-mgr (= ${binary:Version}), - python-six, - ${misc:Depends}, - ${python:Depends}, - ${shlibs:Depends}, - Description: rook plugin for ceph-mgr ++Depends: ++ ceph-mgr (>= ${binary:Version}), ++ python3-numpy, ++ python3-scipy, ++ python3-sklearn, ++ ${misc:Depends}, ++ ${python3:Depends}, ++ ${shlibs:Depends}, ++Description: diskprediction-local plugin for ceph-mgr + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . - This package contains the rook plugin for ceph-mgr's orchestration - functionality, to allow ceph-mgr to install and configure ceph using - Rook. ++ This package contains the diskprediction_local plugin for the ceph-mgr ++ daemon, which helps predict disk failures. + +Package: ceph-mgr-k8sevents +Architecture: all - Depends: ceph-mgr (= ${binary:Version}), - python-kubernetes, - ${misc:Depends}, - ${python:Depends}, ++Depends: ++ ceph-mgr (>= ${binary:Version}), ++ python3-kubernetes, ++ ${misc:Depends}, ++ ${python:Depends}, +Description: kubernetes events plugin for ceph-mgr + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains the k8sevents plugin, to allow ceph-mgr to send + ceph related events to the kubernetes events API, and track all events + that occur within the rook-ceph namespace. + - Package: ceph-mgr-ssh ++Package: ceph-mgr-rook +Architecture: all - Depends: ceph-mgr (= ${binary:Version}), - python-six, - ${misc:Depends}, - ${python:Depends}, - Description: ssh orchestrator plugin for ceph-mgr ++Depends: ++ ceph-mgr (>= ${binary:Version}), ++ python3-six, ++ ${misc:Depends}, ++ ${python3:Depends}, ++ ${shlibs:Depends}, ++Description: rook plugin for ceph-mgr + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . - This package contains the SSH plugin for ceph-mgr's orchestration - functionality, to allow ceph-mgr to perform orchestration functions - over a standard SSH connection. ++ This package contains the rook plugin for ceph-mgr's orchestration ++ functionality, to allow ceph-mgr to install and configure ceph using ++ Rook. + - Package: ceph-mgr-dbg - Architecture: linux-any - Section: debug - Priority: extra - Depends: ceph-mgr (= ${binary:Version}), - ${misc:Depends}, - Description: debugging symbols for ceph-mgr ++Package: ceph-mgr-ssh ++Architecture: all ++Depends: ++ ceph-mgr (>= ${binary:Version}), ++ python3-six, ++ ${misc:Depends}, ++ ${python3:Depends}, ++Description: ssh orchestrator plugin for ceph-mgr + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . - This package contains the debugging symbols for ceph-mgr. ++ This package contains the SSH plugin for ceph-mgr's orchestration ++ functionality, to allow ceph-mgr to perform orchestration functions ++ over a standard SSH connection. + +Package: ceph-mon +Architecture: linux-any - Depends: ceph-base (= ${binary:Version}), - ${misc:Depends}, - ${shlibs:Depends}, - Replaces: ceph (<< 10), ceph-test (<< 12.2.2-14) - Breaks: ceph (<< 10), ceph-test (<< 12.2.2-14) ++Depends: ++ ceph-base (= ${binary:Version}), ++ ${misc:Depends}, ++ ${python3:Depends}, ++ ${shlibs:Depends}, ++Replaces: ++ ceph (<< 10.2.2-0ubuntu2~), ++Breaks: ++ ceph (<< 10.2.2-0ubuntu2~), +Description: monitor server for the ceph storage system + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains the cluster monitor daemon for the Ceph storage + system. One or more instances of ceph-mon form a Paxos part-time parliament + cluster that provides extremely reliable and durable storage of cluster + membership, configuration, and state. + - Package: ceph-mon-dbg - Architecture: linux-any - Section: debug - Priority: extra - Depends: ceph-mon (= ${binary:Version}), - ${misc:Depends}, - Description: debugging symbols for ceph-mon - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. - . - This package contains the debugging symbols for ceph-mon. - +Package: ceph-osd +Architecture: linux-any - Depends: ceph-base (= ${binary:Version}), - lvm2, - sudo, - ${misc:Depends}, - ${python:Depends}, - ${shlibs:Depends}, - Replaces: ceph (<< 10), ceph-test (<< 12.2.2-14) - Breaks: ceph (<< 10), ceph-test (<< 12.2.2-14) - Recommends: nvme-cli, - smartmontools, ++Depends: ++ ceph-base (= ${binary:Version}), ++ lvm2, ++ smartmontools (>= 7.0), ++ sudo, ++ ${misc:Depends}, ++ ${python3:Depends}, ++ ${shlibs:Depends}, ++Suggests: ++ nvme-cli, ++Pre-Depends: ++ ceph-common (= ${binary:Version}), ++Replaces: ++ ceph (<< 10.2.2-0ubuntu2~), ++ ceph-test (<< 12.2.8+dfsg1-1~), ++Breaks: ++ ceph (<< 10.2.2-0ubuntu2~), ++ ceph-test (<< 12.2.8+dfsg1-1~), +Description: OSD server for the ceph storage system + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains the Object Storage Daemon for the Ceph storage system. + It is responsible for storing objects on a local file system + and providing access to them over the network. + - Package: ceph-osd-dbg - Architecture: linux-any - Section: debug - Priority: extra - Depends: ceph-osd (= ${binary:Version}), - ${misc:Depends}, - Description: debugging symbols for ceph-osd - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. - . - This package contains the debugging symbols for ceph-osd. - - Package: ceph-fuse - Architecture: linux-any - Depends: ${misc:Depends}, - ${shlibs:Depends}, - ${python3:Depends}, - fuse, - Description: FUSE-based client for the Ceph distributed file system - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. This is a - FUSE-based client that allows one to mount a Ceph file system without - root privileges. - . - Because the FUSE-based client has certain inherent performance - limitations, it is recommended that the native Linux kernel client - be used if possible. If it is not practical to load a kernel module - (insufficient privileges, older kernel, etc.), then the FUSE client will - do. - - Package: ceph-fuse-dbg - Architecture: linux-any - Section: debug - Priority: extra - Depends: ceph-fuse (= ${binary:Version}), - ${misc:Depends}, - Description: debugging symbols for ceph-fuse - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. This is a - FUSE-based client that allows one to mount a Ceph file system without - root privileges. - . - This package contains the debugging symbols for ceph-fuse. - - Package: rbd-fuse - Architecture: linux-any - Depends: ${misc:Depends}, - ${shlibs:Depends}, - Recommends: fuse, - Description: FUSE-based rbd client for the Ceph distributed file system - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. This is a - FUSE-based client that allows one to map Ceph rbd images as files. - . - FUSE base client that allows one to map Ceph rbd images as files. - - Package: rbd-fuse-dbg - Architecture: linux-any - Section: debug - Priority: extra - Depends: rbd-fuse (= ${binary:Version}), - ${misc:Depends}, - Description: debugging symbols for rbd-fuse - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. This is a - FUSE-based client that allows one to map Ceph rbd images as files. - . - This package contains the debugging symbols for rbd-fuse. - - Package: rbd-mirror - Architecture: linux-any - Depends: ceph-common (= ${binary:Version}), - librados2 (= ${binary:Version}), - ${misc:Depends}, - ${shlibs:Depends}, - Description: Ceph daemon for mirroring RBD images - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. This is a - daemon for mirroring RBD images between Ceph clusters, streaming - changes asynchronously. - - Package: rbd-mirror-dbg - Architecture: linux-any - Section: debug - Priority: extra - Depends: rbd-mirror (= ${binary:Version}), - ${misc:Depends}, - Description: debugging symbols for rbd-mirror - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. This is a - daemon for mirroring RBD images between Ceph clusters, streaming - changes asynchronously. ++Package: ceph-resource-agents ++Architecture: all ++Priority: optional ++Recommends: ++ pacemaker, ++Depends: ++ ceph (>= ${binary:Version}), ++ resource-agents, ++ ${misc:Depends}, ++Description: OCF-compliant resource agents for Ceph ++ Ceph is a distributed storage and network file system designed to provide ++ excellent performance, reliability, and scalability. + . - This package contains the debugging symbols for rbd-mirror. ++ This package contains the resource agents (RAs) which integrate ++ Ceph with OCF-compliant cluster resource managers, ++ such as Pacemaker. + - Package: rbd-nbd - Architecture: linux-any - Depends: ${misc:Depends}, - ${shlibs:Depends}, - Description: NBD-based rbd client for the Ceph distributed file system ++Package: cephfs-shell ++Architecture: all ++Depends: ++ ${misc:Depends}, ++ ${python3:Depends}, ++Description: interactive shell for the Ceph distributed file system + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, - block and file system storage. This is a - NBD-based client that allows one to map Ceph rbd images as local - block device. ++ block and file system storage. This is an interactive tool that ++ allows accessing a Ceph file system without mounting it by providing ++ a nice pseudo-shell which works like an FTP client. + . - NBD base client that allows one to map Ceph rbd images as local - block device. ++ This package contains a CLI for interacting with the CephFS. + - Package: rbd-nbd-dbg ++Package: libcephfs-dev +Architecture: linux-any - Section: debug - Priority: extra - Depends: rbd-nbd (= ${binary:Version}), - ${misc:Depends}, - Description: debugging symbols for rbd-nbd - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. This is a - NBD-based client that allows one to map Ceph rbd images as local - block device. ++Section: libdevel ++Depends: ++ libcephfs2 (= ${binary:Version}), ++ ${misc:Depends}, ++Conflicts: ++ libceph-dev, ++ libceph1-dev, ++ libcephfs2-dev, ++Replaces: ++ libceph-dev, ++ libceph1-dev, ++ libcephfs2-dev, ++Description: Ceph distributed file system client library (development files) ++ Ceph is a distributed network file system designed to provide ++ excellent performance, reliability, and scalability. This is a ++ shared library allowing applications to access a Ceph distributed ++ file system via a POSIX-like interface. + . - This package contains the debugging symbols for rbd-nbd. - - Package: ceph-common - Architecture: linux-any - Depends: librbd1 (= ${binary:Version}), - python-cephfs (= ${binary:Version}), - python-ceph-argparse (= ${binary:Version}), - python-prettytable, - python-rados (= ${binary:Version}), - python-rbd (= ${binary:Version}), - python-rgw (= ${binary:Version}), - ${misc:Depends}, - ${python:Depends}, - ${shlibs:Depends}, - Conflicts: ceph-client-tools, - Replaces: ceph (<< 10), - ceph-client-tools, - ceph-fs-common (<< 11.0), - ceph-test (<< 9.0.3-1646), - librbd1 (<< 0.92-1238), - python-ceph (<< 0.92-1223), - radosgw (<< 12.0.3) - Breaks: ceph (<< 10), - ceph-fs-common (<< 11.0), - ceph-test (<< 9.0.3-1646), - librbd1 (<< 0.92-1238), - python-ceph (<< 0.92-1223), - radosgw (<< 12.0.3) - Suggests: ceph-base (= ${binary:Version}), - ceph-mds (= ${binary:Version}), - Description: common utilities to mount and interact with a ceph storage cluster - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. This is a collection - of common tools that allow one to interact with and administer a Ceph cluster. ++ This package contains development files needed for building applications that ++ link against libcephfs2. + - Package: ceph-common-dbg - Architecture: linux-any - Depends: ceph-common (= ${binary:Version}), - ${misc:Depends}, - Conflicts: ceph-client-tools-dbg, - Replaces: ceph-client-tools-dbg, - ceph-test-dbg (<< 9.0.3-1646), - Breaks: ceph-test-dbg (<< 9.0.3-1646), - Section: debug - Priority: extra - Description: debugging symbols for ceph-common - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. This is a collection - of common tools that allow one to interact with and administer a Ceph cluster. ++Package: libcephfs-java ++Architecture: all ++Section: java ++Depends: ++ libcephfs-jni (>= ${binary:Version}), ++ ${java:Depends}, ++ ${misc:Depends}, ++Description: Java library for the Ceph File System ++ Ceph is a distributed storage system designed to provide excellent ++ performance, reliability, and scalability. + . - This package contains the debugging symbols for ceph-common. ++ This package contains the Java library for interacting with the Ceph ++ File System. + - Package: ceph-resource-agents ++Package: libcephfs-jni +Architecture: linux-any - Recommends: pacemaker, - Priority: extra - Depends: ceph (= ${binary:Version}), - resource-agents, - ${misc:Depends}, - Description: OCF-compliant resource agents for Ceph - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. ++Section: libs ++Depends: ++ libcephfs2 (= ${binary:Version}), ++ ${misc:Depends}, ++ ${shlibs:Depends}, ++Description: Java Native Interface library for CephFS Java bindings ++ Ceph is a distributed storage system designed to provide excellent ++ performance, reliability, and scalability. + . - This package contains the resource agents (RAs) which integrate - Ceph with OCF-compliant cluster resource managers, - such as Pacemaker. ++ This package contains the Java Native Interface library for interacting ++ with the Ceph File System. + - Package: librados2 - Conflicts: librados - Replaces: librados ++Package: libcephfs2 +Architecture: linux-any +Section: libs - Depends: ${misc:Depends}, - ${shlibs:Depends}, - Description: RADOS distributed object store client library - RADOS is a reliable, autonomic distributed object storage cluster - developed as part of the Ceph distributed storage system. This is a - shared library allowing applications to access the distributed object - store using a simple file-like interface. ++Conflicts: ++ libceph, ++ libceph1, ++ libcephfs, ++Replaces: ++ libceph, ++ libceph1, ++ libcephfs, ++Depends: ++ ${misc:Depends}, ++ ${shlibs:Depends}, ++Pre-Depends: ++ ${misc:Pre-Depends}, ++Description: Ceph distributed file system client library ++ Ceph is a distributed network file system designed to provide ++ excellent performance, reliability, and scalability. This is a ++ shared library allowing applications to access a Ceph distributed ++ file system via a POSIX-like interface. + - Package: librados2-dbg ++Package: librados-dev +Architecture: linux-any - Section: debug - Priority: extra - Depends: librados2 (= ${binary:Version}), - ${misc:Depends}, - Description: debugging symbols for librados ++Section: libdevel ++Depends: ++ librados2 (= ${binary:Version}), ++ ${misc:Depends}, ++ ${shlibs:Depends}, ++Conflicts: ++ librados1-dev, ++ librados2-dev, ++Replaces: ++ librados1-dev, ++ librados2-dev, ++Description: RADOS distributed object store client library (development files) + RADOS is a reliable, autonomic distributed object storage cluster + developed as part of the Ceph distributed storage system. This is a + shared library allowing applications to access the distributed object + store using a simple file-like interface. + . - This package contains debugging symbols for librados. ++ This package contains development files needed for building applications that ++ link against librados2. + - Package: librados-dev ++Package: librados2 +Architecture: linux-any - Section: libdevel - Depends: librados2 (= ${binary:Version}), - ${misc:Depends}, - ${shlibs:Depends}, - Conflicts: librados1-dev, - librados2-dev, - Replaces: librados1-dev, - librados2-dev, - Description: RADOS distributed object store client library (development files) ++Section: libs ++Conflicts: ++ librados, ++ librados1, ++Replaces: ++ librados, ++ librados1, ++Depends: ++ ${misc:Depends}, ++ ${shlibs:Depends}, ++Pre-Depends: ++ ${misc:Pre-Depends}, ++Description: RADOS distributed object store client library + RADOS is a reliable, autonomic distributed object storage cluster + developed as part of the Ceph distributed storage system. This is a + shared library allowing applications to access the distributed object + store using a simple file-like interface. - . - This package contains development files needed for building applications that - link against librados. + +Package: libradospp-dev +Architecture: linux-any +Section: libdevel - Depends: librados-dev (= ${binary:Version}), - ${misc:Depends}, - ${shlibs:Depends}, ++Depends: ++ librados-dev (= ${binary:Version}), ++ ${misc:Depends}, ++ ${shlibs:Depends}, +Description: RADOS distributed object store client C++ library (development files) + RADOS is a reliable, autonomic distributed object storage cluster + developed as part of the Ceph distributed storage system. This is a + shared library allowing applications to access the distributed object + store using a simple file-like interface. + . + This package contains development files needed for building C++ applications that + link against librados. + - Package: libradosstriper1 - Architecture: linux-any - Section: libs - Depends: librados2 (= ${binary:Version}), - ${misc:Depends}, - ${shlibs:Depends}, - Description: RADOS striping interface - Striping interface built on top of the rados library, allowing - to stripe bigger objects onto several standard rados objects using - an interface very similar to the rados one. - - Package: libradosstriper1-dbg - Architecture: linux-any - Section: debug - Priority: extra - Depends: libradosstriper1 (= ${binary:Version}), - ${misc:Depends}, - Description: debugging symbols for libradosstriper - libradosstriper is a striping interface built on top of the rados - library, allowing to stripe bigger objects onto several standard - rados objects using an interface very similar to the rados one. - . - This package contains debugging symbols for libradosstriper. - +Package: libradosstriper-dev +Architecture: linux-any +Section: libdevel - Depends: libradosstriper1 (= ${binary:Version}), - ${misc:Depends}, ++Depends: ++ libradosstriper1 (= ${binary:Version}), ++ ${misc:Depends}, +Description: RADOS striping interface (development files) + libradosstriper is a striping interface built on top of the rados + library, allowing to stripe bigger objects onto several standard + rados objects using an interface very similar to the rados one. + . + This package contains development files needed for building applications that + link against libradosstriper. + - Package: librbd1 ++Package: libradosstriper1 +Architecture: linux-any +Section: libs - Depends: librados2 (= ${binary:Version}), - ${misc:Depends}, - ${shlibs:Depends}, - Description: RADOS block device client library - RBD is a block device striped across multiple distributed objects - in RADOS, a reliable, autonomic distributed object storage cluster - developed as part of the Ceph distributed storage system. This is a - shared library allowing applications to manage these block devices. - - Package: librbd1-dbg - Architecture: linux-any - Section: debug - Priority: extra - Depends: librbd1 (= ${binary:Version}), - ${misc:Depends}, - Description: debugging symbols for librbd1 - RBD is a block device striped across multiple distributed objects - in RADOS, a reliable, autonomic distributed object storage cluster - developed as part of the Ceph distributed storage system. This is a - shared library allowing applications to manage these block devices. - . - This package contains debugging symbols for librbd1. ++Depends: ++ librados2 (= ${binary:Version}), ++ ${misc:Depends}, ++ ${shlibs:Depends}, ++Description: RADOS striping interface ++ Striping interface built on top of the rados library, allowing ++ to stripe bigger objects onto several standard rados objects using ++ an interface very similar to the rados one. + +Package: librbd-dev +Architecture: linux-any +Section: libdevel - Depends: librados-dev (= ${binary:Version}), - librbd1 (= ${binary:Version}), - ${misc:Depends}, - Conflicts: librbd1-dev, - Replaces: librbd1-dev, ++Depends: ++ librados-dev, ++ librbd1 (= ${binary:Version}), ++ ${misc:Depends}, ++Conflicts: ++ librbd1-dev, ++Replaces: ++ librbd1-dev, +Description: RADOS block device client library (development files) + RBD is a block device striped across multiple distributed objects + in RADOS, a reliable, autonomic distributed object storage cluster + developed as part of the Ceph distributed storage system. This is a + shared library allowing applications to manage these block devices. + . + This package contains development files needed for building applications that + link against librbd1. + - Package: libcephfs2 - Conflicts: libceph, - libceph1, - libcephfs, - Replaces: libceph, - libceph1, - libcephfs, - Architecture: linux-any - Section: libs - Depends: ${misc:Depends}, - ${shlibs:Depends}, - Description: Ceph distributed file system client library - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. This is a - shared library allowing applications to access a Ceph distributed - file system via a POSIX-like interface. - - Package: libcephfs2-dbg - Architecture: linux-any - Section: debug - Priority: extra - Depends: libcephfs2 (= ${binary:Version}), - ${misc:Depends}, - Conflicts: libceph1-dbg, - Replaces: libceph1-dbg, - Description: debugging symbols for libcephfs2 - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. This is a - shared library allowing applications to access a Ceph distributed - file system via a POSIX-like interface. - . - This package contains debugging symbols for libcephfs2. - - Package: libcephfs-dev - Architecture: linux-any - Section: libdevel - Depends: libcephfs2 (= ${binary:Version}), - ${misc:Depends}, - Conflicts: libceph-dev, - libceph1-dev, - libcephfs2-dev, - Replaces: libceph-dev, - libceph1-dev, - libcephfs2-dev, - Description: Ceph distributed file system client library (development files) - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. This is a - shared library allowing applications to access a Ceph distributed - file system via a POSIX-like interface. - . - This package contains development files needed for building applications that - link against libcephfs. - - Package: librgw2 ++Package: librbd1 +Architecture: linux-any +Section: libs - Depends: librados2 (= ${binary:Version}), - ${misc:Depends}, - ${shlibs:Depends}, - Description: RADOS Gateway client library - RADOS is a distributed object store used by the Ceph distributed - storage system. This package provides a REST gateway to the - object store that aims to implement a superset of Amazon's S3 - service. - . - This package contains the library interface and headers only. - - Package: librgw2-dbg - Architecture: linux-any - Section: debug - Priority: extra - Depends: librgw2 (= ${binary:Version}), - ${misc:Depends}, - Description: debugging symbols for librbd1 - RADOS is a distributed object store used by the Ceph distributed - storage system. This package provides a REST gateway to the - object store that aims to implement a superset of Amazon's S3 - service. - . - This package contains debugging symbols for librgw2. ++Depends: ++ librados2 (= ${binary:Version}), ++ ${misc:Depends}, ++ ${shlibs:Depends}, ++Pre-Depends: ++ ${misc:Pre-Depends}, ++Description: RADOS block device client library ++ RBD is a block device striped across multiple distributed objects ++ in RADOS, a reliable, autonomic distributed object storage cluster ++ developed as part of the Ceph distributed storage system. This is a ++ shared library allowing applications to manage these block devices. + +Package: librgw-dev +Architecture: linux-any +Section: libdevel - Depends: librados-dev (= ${binary:Version}), - librgw2 (= ${binary:Version}), - ${misc:Depends}, ++Depends: ++ librados-dev (= ${binary:Version}), ++ librgw2 (= ${binary:Version}), ++ ${misc:Depends}, +Description: RADOS client library (development files) + RADOS is a distributed object store used by the Ceph distributed + storage system. This package provides a REST gateway to the + object store that aims to implement a superset of Amazon's S3 + service. + . + This package contains development files needed for building applications + that link against librgw2. + - Package: radosgw - Architecture: linux-any - Depends: ceph-common (= ${binary:Version}), - librgw2 (= ${binary:Version}), - mime-support, - ${misc:Depends}, - ${shlibs:Depends}, - Recommends: ntp | time-daemon, - Description: REST gateway for RADOS distributed object store - RADOS is a distributed object store used by the Ceph distributed - storage system. This package provides a REST gateway to the - object store that aims to implement a superset of Amazon's S3 - service as well as the OpenStack Object Storage ("Swift") API. - . - This package contains the proxy daemon and related tools only. - - Package: radosgw-dbg ++Package: librgw2 +Architecture: linux-any - Section: debug - Priority: extra - Depends: radosgw (= ${binary:Version}), - ${misc:Depends}, - Description: debugging symbols for radosgw ++Section: libs ++Depends: ++ librados2 (= ${binary:Version}), ++ ${misc:Depends}, ++ ${shlibs:Depends}, ++Description: RADOS Gateway client library + RADOS is a distributed object store used by the Ceph distributed + storage system. This package provides a REST gateway to the + object store that aims to implement a superset of Amazon's S3 - service as well as the OpenStack Object Storage ("Swift") API. - . - This package contains debugging symbols for radosgw. - - Package: ceph-test - Architecture: linux-any - Depends: ceph-common, - curl, - jq, - socat, - xmlstarlet, - ${misc:Depends}, - ${shlibs:Depends}, - Replaces: ceph-base (<< 11) - Breaks: ceph-base (<< 1) - Description: Ceph test and benchmarking tools - This package contains tools for testing and benchmarking Ceph. - - Package: ceph-test-dbg - Architecture: linux-any - Section: debug - Priority: extra - Depends: ceph-common (= ${binary:Version}), - ceph-test (= ${binary:Version}), - curl, - ${misc:Depends}, - Description: Ceph test and benchmarking tools ++ service. + . - This package contains the debugging symbols for ceph-test. ++ This package contains the library interface and headers only. + - Package: python-ceph - Architecture: linux-any ++Package: python3-ceph ++Architecture: all +Section: python - Depends: python-cephfs (= ${binary:Version}), - python-rados (= ${binary:Version}), - python-rbd (= ${binary:Version}), - python-rgw (= ${binary:Version}), - Description: Meta-package for python libraries for the Ceph libraries ++Depends: ++ python3-cephfs (<< ${source:Version}.1~), ++ python3-cephfs (>= ${source:Version}), ++ python3-rados (<< ${source:Version}.1~), ++ python3-rados (>= ${source:Version}), ++ python3-rbd (<< ${source:Version}.1~), ++ python3-rbd (>= ${source:Version}), ++ python3-rgw (<< ${source:Version}.1~), ++ python3-rgw (>= ${source:Version}), ++ ${misc:Depends}, ++Description: Meta-package for all Python 3.x modules for the Ceph libraries + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . - This package is a metapackage for all Python 2 bindings. ++ This package is a metapackage for all Ceph Python 3.x bindings. + - Package: python-rados ++Package: python3-ceph-argparse +Architecture: linux-any +Section: python - Depends: librados2 - ${misc:Depends}, - ${python:Depends}, - ${shlibs:Depends}, - Replaces: python-ceph (<< 0.92-1223), - Breaks: python-ceph (<< 0.92-1223), - Description: Python 2 libraries for the Ceph librados library - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. - . - This package contains Python 2 libraries for interacting with Ceph's - RADOS object storage. - - Package: python-rados-dbg - Architecture: linux-any - Section: debug - Priority: extra - Depends: python-rados (= ${binary:Version}), - python-dbg, - ${misc:Depends}, - Description: Python 2 libraries for the Ceph librados library ++Depends: ++ ${misc:Depends}, ++ ${python3:Depends}, ++Breaks: ++ ceph-common (<< 14.2.1-0~), ++Replaces: ++ ceph-common (<< 14.2.1-0~), ++Description: Python 3 utility libraries for Ceph CLI + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . - This package contains Python 2 libraries for interacting with Ceph's - RADOS object storage. - . - This package contains the debugging symbols for python-rados. ++ This package contains types and routines for Python 3 used by the ++ Ceph CLI as well as the RESTful interface. + - Package: python3-rados ++Package: python3-cephfs +Architecture: linux-any +Section: python - Depends: librados2, - ${misc:Depends}, - ${python3:Depends}, - ${shlibs:Depends}, - Description: Python 3 libraries for the Ceph librados library ++Depends: ++ libcephfs2 (= ${binary:Version}), ++ python3-ceph-argparse (= ${binary:Version}), ++ python3-rados (= ${binary:Version}), ++ ${misc:Depends}, ++ ${python3:Depends}, ++ ${shlibs:Depends}, ++Description: Python 3 libraries for the Ceph libcephfs library + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains Python 3 libraries for interacting with Ceph's - RADOS object storage. ++ CephFS file system client library. + - Package: python3-rados-dbg ++Package: python3-rados +Architecture: linux-any - Section: debug - Priority: extra - Depends: python3-rados (= ${binary:Version}), - python3-dbg, - ${misc:Depends} ++Section: python ++Depends: ++ librados2 (= ${binary:Version}), ++ ${misc:Depends}, ++ ${python3:Depends}, ++ ${shlibs:Depends}, +Description: Python 3 libraries for the Ceph librados library + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains Python 3 libraries for interacting with Ceph's + RADOS object storage. - . - This package contains the debugging symbols for python-rados. - - Package: python-rbd - Architecture: linux-any - Section: python - Depends: librbd1 (>= ${binary:Version}), - ${misc:Depends}, - ${python:Depends}, - ${shlibs:Depends}, - Replaces: python-ceph (<< 0.92-1223), - Breaks: python-ceph (<< 0.92-1223), - Description: Python 2 libraries for the Ceph librbd library - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. - . - This package contains Python 2 libraries for interacting with Ceph's - RBD block device library. - - Package: python-rbd-dbg - Architecture: linux-any - Section: debug - Priority: extra - Depends: python-rbd (= ${binary:Version}), - python-dbg, - ${misc:Depends}, - Description: Python 2 libraries for the Ceph librbd library - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. - . - This package contains Python 2 libraries for interacting with Ceph's - RBD block device library. - . - This package contains the debugging symbols for python-rbd. + +Package: python3-rbd +Architecture: linux-any +Section: python - Depends: librbd1 (>= ${binary:Version}), - ${misc:Depends}, - ${python3:Depends}, - ${shlibs:Depends}, - Description: Python 3 libraries for the Ceph librbd library - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. - . - This package contains Python 3 libraries for interacting with Ceph's - RBD block device library. - - Package: python3-rbd-dbg - Architecture: linux-any - Section: debug - Priority: extra - Depends: python3-rbd (= ${binary:Version}), - python3-dbg, - ${misc:Depends}, ++Depends: ++ librbd1 (>= ${binary:Version}), ++ ${misc:Depends}, ++ ${python3:Depends}, ++ ${shlibs:Depends}, +Description: Python 3 libraries for the Ceph librbd library + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains Python 3 libraries for interacting with Ceph's + RBD block device library. - . - This package contains the debugging symbols for python-rbd. - - Package: python-rgw - Architecture: linux-any - Section: python - Depends: librgw2 (>= ${binary:Version}), - python-rados (= ${binary:Version}), - ${misc:Depends}, - ${python:Depends}, - ${shlibs:Depends}, - Replaces: python-ceph (<< 0.92-1223), - Breaks: python-ceph (<< 0.92-1223), - Description: Python 2 libraries for the Ceph librgw library - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. - . - This package contains Python 2 libraries for interacting with Ceph's - RGW library. - - Package: python-rgw-dbg - Architecture: linux-any - Section: debug - Priority: extra - Depends: python-rgw (= ${binary:Version}), - python-dbg, - ${misc:Depends}, - Description: Python 2 libraries for the Ceph librgw library - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. - . - This package contains Python 2 libraries for interacting with Ceph's - RGW library. - . - This package contains the debugging symbols for python-rgw. + +Package: python3-rgw +Architecture: linux-any +Section: python - Depends: librgw2 (>= ${binary:Version}), - python3-rados (= ${binary:Version}), - ${misc:Depends}, - ${python3:Depends}, - ${shlibs:Depends}, ++Depends: ++ librgw2 (>= ${binary:Version}), ++ python3-rados (= ${binary:Version}), ++ ${misc:Depends}, ++ ${python3:Depends}, ++ ${shlibs:Depends}, +Description: Python 3 libraries for the Ceph librgw library + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, + block and file system storage. + . + This package contains Python 3 libraries for interacting with Ceph's + RGW library. + - Package: python3-rgw-dbg - Architecture: linux-any - Section: debug - Priority: extra - Depends: python3-rgw (= ${binary:Version}), - python3-dbg, - ${misc:Depends}, - Description: Python 3 libraries for the Ceph librgw library - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. - . - This package contains Python 3 libraries for interacting with Ceph's - RGW library. - . - This package contains the debugging symbols for python3-rgw. - - Package: python-cephfs ++Package: rados-objclass-dev +Architecture: linux-any - Section: python - Depends: libcephfs2 (= ${binary:Version}), - python-rados (= ${binary:Version}), - python-ceph-argparse (= ${binary:Version}), - ${misc:Depends}, - ${python:Depends}, - ${shlibs:Depends}, - Replaces: python-ceph (<< 0.92-1223), - Breaks: python-ceph (<< 0.92-1223), - Description: Python 2 libraries for the Ceph libcephfs library - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. ++Section: libdevel ++Depends: ++ librados-dev (= ${binary:Version}), ++ ${misc:Depends}, ++Description: RADOS object class development kit. + . - This package contains Python 2 libraries for interacting with Ceph's - CephFS file system client library. ++ This package contains development files needed for building RADOS object class plugins. + - Package: python-cephfs-dbg ++Package: radosgw +Architecture: linux-any - Section: debug - Priority: extra - Depends: python-cephfs (= ${binary:Version}), - python-dbg, - ${misc:Depends}, - Description: Python 2 libraries for the Ceph libcephfs library - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. - . - This package contains Python 2 libraries for interacting with Ceph's - CephFS file system client library. ++Depends: ++ ceph-common (= ${binary:Version}), ++ librgw2 (= ${binary:Version}), ++ ${misc:Depends}, ++ ${shlibs:Depends}, ++Suggests: ++ logrotate, ++Description: REST gateway for RADOS distributed object store ++ RADOS is a distributed object store used by the Ceph distributed ++ storage system. This package provides a REST gateway to the ++ object store that aims to implement a superset of Amazon's S3 ++ service as well as the OpenStack Object Storage ("Swift") API. + . - This package contains the debugging symbols for python-cephfs. ++ This package contains the proxy daemon and related tools only. + - Package: python3-cephfs ++Package: rbd-fuse +Architecture: linux-any - Section: python - Depends: libcephfs2 (= ${binary:Version}), - python3-rados (= ${binary:Version}), - python3-ceph-argparse (= ${binary:Version}), - ${misc:Depends}, - ${python3:Depends}, - ${shlibs:Depends}, - Description: Python 3 libraries for the Ceph libcephfs library - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. - . - This package contains Python 3 libraries for interacting with Ceph's - CephFS file system client library. ++Depends: ++ ${misc:Depends}, ++ ${shlibs:Depends}, ++Recommends: ++ fuse, ++Description: FUSE-based rbd client for the Ceph distributed file system ++ Ceph is a distributed network file system designed to provide ++ excellent performance, reliability, and scalability. This is a ++ FUSE-based client that allows one to map Ceph rbd images as files. + - Package: python3-cephfs-dbg ++Package: rbd-mirror +Architecture: linux-any - Section: debug - Priority: extra - Depends: python3-cephfs (= ${binary:Version}), - python3-dbg, - ${misc:Depends}, - Description: Python 3 libraries for the Ceph libcephfs library - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. - . - This package contains Python 3 libraries for interacting with Ceph's - CephFS file system client library. - . - This package contains the debugging symbols for python3-cephfs. - - Package: python-ceph-argparse - Architecture: all - Section: python - Depends: ${misc:Depends}, - ${python:Depends}, - Replaces: ceph-common (<< 14.0.0) - Breaks: ceph-common (<< 14.0.0) - Description: Python 2 utility libraries for Ceph CLI - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. - . - This package contains types and routines for Python 2 used by the - Ceph CLI as well as the RESTful interface. - - Package: python3-ceph-argparse - Architecture: all - Section: python - Depends: ${misc:Depends}, - ${python3:Depends}, - Description: Python 3 utility libraries for Ceph CLI - Ceph is a massively scalable, open-source, distributed - storage system that runs on commodity hardware and delivers object, - block and file system storage. ++Depends: ++ ceph-common (= ${binary:Version}), ++ librados2 (= ${binary:Version}), ++ ${misc:Depends}, ++ ${shlibs:Depends}, ++Description: Ceph daemon for mirroring RBD images ++ Ceph is a distributed storage system designed to provide excellent ++ performance, reliability, and scalability. + . - This package contains types and routines for Python 3 used by the - Ceph CLI as well as the RESTful interface. - - Package: libcephfs-java - Section: java - Architecture: all - Depends: libcephfs-jni (= ${binary:Version}), - ${java:Depends}, - ${misc:Depends}, - Description: Java libraries for the Ceph File System ++ This package provides a daemon for mirroring RBD images between ++ Ceph clusters, streaming changes asynchronously. + - Package: libcephfs-jni - Architecture: linux-any - Section: java - Depends: libcephfs2 (= ${binary:Version}), - ${java:Depends}, - ${misc:Depends}, - ${shlibs:Depends}, - Description: Java Native Interface library for CephFS Java bindings - - Package: rados-objclass-dev ++Package: rbd-nbd +Architecture: linux-any - Section: libdevel - Depends: librados-dev (= ${binary:Version}) ${misc:Depends}, - Description: RADOS object class development kit. - . - This package contains development files needed for building RADOS object class plugins. - - Package: cephfs-shell - Architecture: all - Depends: ${misc:Depends} - ${python3:Depends} - Description: interactive shell for the Ceph distributed file system ++Depends: ++ ${misc:Depends}, ++ ${shlibs:Depends}, ++Description: NBD-based rbd client for the Ceph distributed file system + Ceph is a massively scalable, open-source, distributed + storage system that runs on commodity hardware and delivers object, - block and file system storage. This is an interactive tool that - allows accessing a Ceph file system without mounting it by providing - a nice pseudo-shell which works like an FTP client. ++ block and file system storage. This is a ++ NBD-based client that allows one to map Ceph rbd images as local ++ block device. + . - This package contains a CLI for interacting with the CephFS. ++ NBD base client that allows one to map Ceph rbd images as local ++ block device. diff --cc debian/copyright index afb188d53,000000000..390a68a96 mode 100644,000000..100644 --- a/debian/copyright +++ b/debian/copyright @@@ -1,149 -1,0 +1,977 @@@ +Format-Specification: http://anonscm.debian.org/viewvc/dep/web/deps/dep5/copyright-format.xml?revision=279&view=markup +Name: ceph +Maintainer: Sage Weil +Source: http://ceph.com/ + +Files: * - Copyright: (c) 2004-2010 by Sage Weil ++Copyright: 2004-2014 Sage Weil ++ 2004-2014 Inktank ++ Inktank, Inc ++ Inktank Storage, Inc. ++ 2012-2014 Red Hat ++ 2013-2014 Cloudwatt ++ 2013 CohortFS, LLC ++ 2004-2011 Dreamhost ++ 2013 eNovance SAS ++ 2014 Adam Crume ++ 2012 Florian Haas, hastexo ++ 2010 Greg Farnum ++ 2014 John Spray ++ 2011 Stanislav Sedov ++ 2013-2014 UnitedStack ++ 2011 Wido den Hollander +License: LGPL2.1 (see COPYING-LGPL2.1) + +Files: cmake/modules/FindLTTngUST.cmake +Copyright: + Copyright 2016 Kitware, Inc. + Copyright 2016 Philippe Proulx +License: BSD 3-clause + +Files: doc/* +Copyright: (c) 2010-2012 New Dream Network and contributors +License: Creative Commons Attribution Share Alike 3.0 (CC-BY-SA-3.0) + ++Files: bin/git-archive-all.sh ++License: GPL3 ++ +Files: src/mount/canonicalize.c +Copyright: Copyright (C) 1993 Rick Sladkey +License: LGPL2 or later (see COPYING-GPL2) + ++ +Files: src/os/btrfs_ioctl.h +Copyright: Copyright (C) 2007 Oracle. All rights reserved. +License: GPL2 (see COPYING-GPL2) + +Files: src/include/ceph_hash.cc +Copyright: None +License: Public domain + +Files: src/common/bloom_filter.hpp - Copyright: Copyright (C) 2000 Arash Partow ++Copyright: Copyright (C) 2000 Arash Partow +License: Boost Software License, Version 1.0 + +Files: src/common/crc32c_intel*: +Copyright: + Copyright 2012-2013 Intel Corporation All Rights Reserved. +License: BSD 3-clause + +Files: src/common/sctp_crc32.c: +Copyright: + Copyright (c) 2001-2007, by Cisco Systems, Inc. All rights reserved. + Copyright (c) 2004-2006 Intel Corporation - All Rights Reserved +License: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + a) Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + b) Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + c) Neither the name of Cisco Systems, Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + +Files: src/json_spirit +Copyright: + Copyright John W. Wilkinson 2007 - 2011 +License: + The MIT License + + Copyright (c) 2007 - 2010 John W. Wilkinson + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + - - +Files: src/test/common/Throttle.cc src/test/filestore/chain_xattr.cc +Copyright: Copyright (C) 2013 Cloudwatt +License: LGPL2.1 or later + ++ ++ +Files: src/osd/ErasureCodePluginJerasure/*.{c,h} +Copyright: Copyright (c) 2011, James S. Plank +License: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + - Neither the name of the University of Tennessee nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + - Packaging: - Copyright (C) 2004-2009 by Sage Weil - Copyright (C) 2010 Canonical, Ltd. - Licensed under LGPL-2.1 ++ ++Files: qa/workunits/erasure-code/jquery.js ++Copyright: 2012 jQuery Foundation and other contributors ++License: MIT ++ ++Files: qa/workunits/erasure-code/jquery.{flot.categories,flot}.js ++Copyright: 2007-2014 IOLA and Ole Laursen. ++License: MIT ++ ++Files: src/include/timegm.h ++Copyright: Howard Hinnant ++ 2010-2011 Vicente J. Botet Escriba ++License: Boost Software License, Version 1.0 ++ ++Files: src/pybind/mgr/diskprediction_local/models/* ++Copyright: None ++License: Public domain ++ ++Files: src/ceph-volume/plugin/zfs/* ++Copyright: 2018, Willem Jan Withagen ++License: BSD 3-clause ++ + +Files: src/test/perf_local.cc +Copyright: + (c) 2011-2014 Stanford University + (c) 2011 Facebook +License: + The MIT License ++ ++ ++Comment: ----------------------------------------------- ++ Content above is taken from upstream's COPYING file. ++ Unfortunately it is incomplete. Debian/Ubuntu packaging ++ findings/additions are below. ++ ------------------------------------------------------- ++ ++ ++Files: src/erasure-code/jerasure/ErasureCode* ++ src/erasure-code/ErasureCode* ++ src/erasure-code/isa/* ++ src/include/str_map.h ++ src/test/common/test_str_map.cc ++ src/test/erasure-code/* ++ src/test/rgw/test_rgw_manifest.cc ++Copyright: 2014 CERN/Switzerland ++ 2013-2014 Cloudwatt ++ 2014 Red Hat ++ 2013 eNovance SAS ++License: LGPL-2.1+ ++ ++ ++Files: src/erasure-code/isa/isa-l/erasure_code/* ++Copyright: 2011-2014 Intel Corporation ++License: BSD-3-clause ++ ++Files: src/rocksdb/* ++Copyright: 2004-2013 Facebook, Inc. ++ 2011 The LevelDB Authors ++ 2009 Google Inc. ++License: BSD-3-clause ++Comment: ++ Additional Grant of Patent Rights ++ . ++ “Software” means the rocksdb software distributed by Facebook, Inc. ++ . ++ Facebook hereby grants you a perpetual, worldwide, royalty-free, ++ non-exclusive, irrevocable (subject to the termination provision below) ++ license under any rights in any patent claims owned by Facebook, to make, ++ have made, use, sell, offer to sell, import, and otherwise transfer the ++ Software. For avoidance of doubt, no license is granted under Facebook’s ++ rights in any patent claims that are infringed by (i) modifications to the ++ Software made by you or a third party, or (ii) the Software in combination ++ with any software or other technology provided by you or a third party. ++ . ++ The license granted hereunder will terminate, automatically and without ++ notice, for anyone that makes any claim (including by filing any lawsuit, ++ assertion or other action) alleging (a) direct, indirect, or contributory ++ infringement or inducement to infringe any patent: (i) by Facebook or any ++ of its subsidiaries or affiliates, whether or not such claim is related ++ to the Software, (ii) by any party if such claim arises in whole or in ++ part from any software, product or service of Facebook or any of its ++ subsidiaries or affiliates, whether or not such claim is related to the ++ Software, or (iii) by any party relating to the Software; or (b) that ++ any right in any patent claim of Facebook is invalid or unenforceable. ++ ++ ++Files: src/rocksdb/util/xxhash.* ++Copyright: 2012-2014, Yann Collet. ++License: BSD-2-clause ++ Redistribution and use in source and binary forms, with or without ++ modification, are permitted provided that the following conditions are ++ met: ++ . ++ * Redistributions of source code must retain the above copyright ++ notice, this list of conditions and the following disclaimer. ++ * Redistributions in binary form must reproduce the above ++ copyright notice, this list of conditions and the following disclaimer ++ in the documentation and/or other materials provided with the ++ distribution. ++ . ++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ ++ ++ ++Files: src/mount/canonicalize.c ++ src/test/common/test_config.cc ++ src/test/crush/TestCrushWrapper.cc ++ src/test/common/Throttle.cc ++ src/test/objectstore/chain_xattr.cc ++ src/test/mon/mon-test-helpers.sh ++ src/test/objectstore/chain_xattr.cc ++ src/test/osd/osd-test-helpers.sh ++ src/ceph-disk ++ src/stop.sh ++Copyright: 1993 Rick Sladkey ++ 2013 Inktank ++ 2013-2014 Cloudwatt ++License: LGPL-2+ ++ ++Files: src/os/btrfs_ioctl.h ++ src/test/mon/PGMap.cc ++Copyright: 2007 Oracle. All rights reserved. ++ 2014 Inktank ++License: GPL-2 ++ ++Files: src/common/ceph_hash.cc ++Copyright: 1995-1997 Robert J. Jenkins Jr. ++License: public-domain ++ This file uses Robert Jenkin's hash function as detailed at: ++ . ++ http://burtleburtle.net/bob/hash/evahash.html ++ . ++ This is in the public domain. ++ ++Files: src/common/bloom_filter.hpp ++Copyright: 2000 Arash Partow ++License: Boost-Software-License-1.0 ++ Permission is hereby granted, free of charge, to any person or organization ++ obtaining a copy of the software and accompanying documentation covered by ++ this license (the "Software") to use, reproduce, display, distribute, ++ execute, and transmit the Software, and to prepare derivative works of the ++ Software, and to permit third-parties to whom the Software is furnished to ++ do so, all subject to the following: ++ . ++ The copyright notices in the Software and this entire statement, ++ including the above license grant, this restriction and the following ++ disclaimer, must be included in all copies of the Software, in whole or ++ in part, and all derivative works of the Software, unless such copies ++ or derivative works are solely in the form of machine-executable object ++ code generated by a source language processor. ++ . ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT ++ SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ++ FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ++ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ DEALINGS IN THE SOFTWARE. ++ ++Files: src/common/crc32c_intel* ++Copyright: 2012-2013 Intel Corporation All Rights Reserved. ++License: BSD-3-clause ++ ++Files: src/common/sctp_crc32.c ++Copyright: 2001-2007, by Cisco Systems, Inc. All rights reserved, ++ 2004-2006 Intel Corporation - All Rights Reserved ++License: BSD-3-clause ++ ++Files: src/erasure-code/jerasure/gf-complete/*/* ++Copyright: 2013 James S. Plank ++ Ethan L. Miller ++ Kevin M. Greenan ++ Benjamin A. Arnold ++ John A. Burnum ++ Adam W. Disney ++ Allen C. McBride ++License: BSD-3-clause ++Comment: ++ https://bitbucket.org/jimplank/gf-complete ++ ++Files: src/erasure-code/jerasure/jerasure/*/* ++Copyright: 2011-2013 James S. Plank ++ 2013 Kevin Greenan ++License: BSD-3-clause ++ ++Files: src/gtest/* ++Copyright: 2008, Google Inc. ++License: BSD-3-clause ++ ++Files: src/civetweb/* ++Copyright: 2004-2013 Sergey Lyubka ++ 2013-2014 the Civetweb developers ++License: Expat ++ ++Files: src/json_spirit/* ++Copyright: 2007-2011, John W. Wilkinson ++License: Expat ++ ++Files: src/java/native/ScopedLocalRef.h ++ src/java/native/JniConstants.* ++Copyright: 2010 The Android Open Source Project ++License: Apache-2.0 ++ Licensed under the Apache License, Version 2.0 (the "License"); ++ you may not use this file except in compliance with the License. ++ You may obtain a copy of the License at ++ . ++ http://www.apache.org/licenses/LICENSE-2.0 ++ . ++ Unless required by applicable law or agreed to in writing, software ++ distributed under the License is distributed on an "AS IS" BASIS, ++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ See the License for the specific language governing permissions and ++ limitations under the License. ++ . ++ The complete text of the Apache License, Version 2.0 ++ can be found in "/usr/share/common-licenses/Apache-2.0". ++ ++Files: src/libs3/* ++Copyright: 2008 Bryan Ischo ++License: GPL-3/OpenSSL ++ libs3 is free software: you can redistribute it and/or modify it under the ++ terms of the GNU General Public License as published by the Free Software ++ Foundation, version 3 of the License. ++ . ++ In addition, as a special exception, the copyright holders give ++ permission to link the code of this library and its programs with the ++ OpenSSL library, and distribute linked combinations including the two. ++ . ++ libs3 is distributed in the hope that it will be useful, but WITHOUT ANY ++ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ++ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ++ details. ++ . ++ The complete text of the GNU General Public License version 3 ++ can be found in "/usr/share/common-licenses/GPL-3' file. ++ ++ ++Files: src/mount/mtab.c ++Copyright: util-linux-ng AUTHORS ++License: GPL-2+ ++Comment: ++ "mount/fstab.c" from line 559: ++ https://git.kernel.org/cgit/utils/util-linux/util-linux.git/tree/mount-deprecated/fstab.c?h=v2.22#n559 ++ https://git.kernel.org/cgit/utils/util-linux/util-linux.git/tree/README.licensing ++ ++Files: src/test/librbd/fsx.c ++Copyright: 1991, NeXT Computer, Inc. ++License: APSL-2.0 ++ The contents of this file constitute Original Code as defined in and ++ are subject to the Apple Public Source License Version 2.0 (the ++ "License"). You may not use this file except in compliance with the ++ License. Please obtain a copy of the License at ++ http://www.opensource.apple.com/apsl/ and read it before using this file. ++ . ++ This Original Code and all software distributed under the License are ++ distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ++ EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ++ INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ++ License for the specific language governing rights and limitations ++ under the License. ++Comment: ++ http://codemonkey.org.uk/projects/fsx/ ++ http://codemonkey.org.uk/projects/fsx/fsx-macosforge/fsx.c ++ ++Files: man/* ++ debian/man/* ++Copyright: 2010-2014, Inktank Storage, Inc. and contributors. ++License: CC-BY-SA-3.0 ++ ++ ++Files: debian/missing-sources/bootstrap.js ++Copyright: 2011-2015 Twitter, Inc ++License: MIT ++ ++Files: debian/missing-sources/two.js ++Copyright: 2012 - 2017 jonobr1 / http://jonobr1.com ++License: MIT ++ ++Files: debian/* ++Copyright: 2010 Sage Weil ++ 2010 Canonical, Ltd. ++ 2011-2013 László Böszörményi (GCS) ++ 2013-2014 James Page ++ 2014 Dmitry Smirnov ++ 2019 Bernd Zeimetz ++License: LGPL-2.1 ++ ++License: GPL-2 ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ . ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ . ++ You should have received a copy of the GNU General Public License along ++ with this program; if not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ . ++ On Debian systems, the complete text of the GNU General Public License ++ version 2 can be found in `/usr/share/common-licenses/GPL-2' file. ++ ++License: GPL-2+ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 2 of the License, or ++ (at your option) any later version. ++ . ++ This package is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ . ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++ . ++ On Debian systems, the complete text of the GNU General Public License ++ version 2 can be found in `/usr/share/common-licenses/GPL-2'. ++ ++License: LGPL-2.1 ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License version 2.1 as published by the Free Software Foundation. ++ . ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ . ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library; if not, write to the Free Software ++ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ . ++ On Debian systems, the complete text of the GNU Lesser General ++ Public License can be found in `/usr/share/common-licenses/LGPL-2.1'. ++ ++License: LGPL-2.1+ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ . ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ . ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library; if not, write to the Free Software ++ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ . ++ On Debian systems, the complete text of the GNU Lesser General ++ Public License can be found in `/usr/share/common-licenses/LGPL-2.1'. ++ ++License: LGPL-2+ ++ This library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License version 2 (or later) as published by the Free Software Foundation. ++ . ++ This library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ . ++ You should have received a copy of the GNU Lesser General Public ++ License along with this library; if not, write to the Free Software ++ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ . ++ On Debian systems, the complete text of the GNU Lesser General ++ Public License 2 can be found in `/usr/share/common-licenses/LGPL-2'. ++ ++License: Expat ++ Permission is hereby granted, free of charge, to any person obtaining a copy ++ of this software and associated documentation files (the "Software"), to deal ++ in the Software without restriction, including without limitation the rights ++ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ copies of the Software, and to permit persons to whom the Software is ++ furnished to do so, subject to the following conditions: ++ . ++ The above copyright notice and this permission notice shall be included in ++ all copies or substantial portions of the Software. ++ . ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ THE SOFTWARE. ++Comment: ++ This license also known as "MIT" however FSF consider "MIT" labelling ++ ambiguous and copyright-format specification recommend to label such license ++ as "Expat". ++ ++License: CC-BY-SA-3.0 ++ Creative Commons Attribution-ShareAlike 3.0 Unported ++ ․ ++ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE ++ LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ++ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ++ ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE ++ INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ++ ITS USE. ++ ․ ++ License ++ ․ ++ THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE ++ COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY ++ COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS ++ AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. ++ ․ ++ BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE ++ TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY ++ BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS ++ CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND ++ CONDITIONS. ++ ․ ++ 1. Definitions ++ ․ ++ a. "Adaptation" means a work based upon the Work, or upon the Work and ++ other pre-existing works, such as a translation, adaptation, derivative ++ work, arrangement of music or other alterations of a literary or ++ artistic work, or phonogram or performance and includes cinematographic ++ adaptations or any other form in which the Work may be recast, ++ transformed, or adapted including in any form recognizably derived from ++ the original, except that a work that constitutes a Collection will not ++ be considered an Adaptation for the purpose of this License. For the ++ avoidance of doubt, where the Work is a musical work, performance or ++ phonogram, the synchronization of the Work in timed-relation with a ++ moving image ("synching") will be considered an Adaptation for the ++ purpose of this License. ++ ․ ++ b. "Collection" means a collection of literary or artistic works, such ++ as encyclopedias and anthologies, or performances, phonograms or ++ broadcasts, or other works or subject matter other than works listed in ++ Section 1(f) below, which, by reason of the selection and arrangement of ++ their contents, constitute intellectual creations, in which the Work is ++ included in its entirety in unmodified form along with one or more other ++ contributions, each constituting separate and independent works in ++ themselves, which together are assembled into a collective whole. A work ++ that constitutes a Collection will not be considered an Adaptation (as ++ defined below) for the purposes of this License. ++ ․ ++ c. "Creative Commons Compatible License" means a license that is listed ++ at http://creativecommons.org/compatiblelicenses that has been approved ++ by Creative Commons as being essentially equivalent to this License, ++ including, at a minimum, because that license: (i) contains terms that ++ have the same purpose, meaning and effect as the License Elements of ++ this License; and, (ii) explicitly permits the relicensing of ++ adaptations of works made available under that license under this ++ License or a Creative Commons jurisdiction license with the same License ++ Elements as this License. ++ ․ ++ d. "Distribute" means to make available to the public the original and ++ copies of the Work or Adaptation, as appropriate, through sale or other ++ transfer of ownership. ++ ․ ++ e. "License Elements" means the following high-level license attributes ++ as selected by Licensor and indicated in the title of this License: ++ Attribution, ShareAlike. ++ ․ ++ f. "Licensor" means the individual, individuals, entity or entities that ++ offer(s) the Work under the terms of this License. ++ ․ ++ g. "Original Author" means, in the case of a literary or artistic work, ++ the individual, individuals, entity or entities who created the Work or ++ if no individual or entity can be identified, the publisher; and in ++ addition (i) in the case of a performance the actors, singers, ++ musicians, dancers, and other persons who act, sing, deliver, declaim, ++ play in, interpret or otherwise perform literary or artistic works or ++ expressions of folklore; (ii) in the case of a phonogram the producer ++ being the person or legal entity who first fixes the sounds of a ++ performance or other sounds; and, (iii) in the case of broadcasts, the ++ organization that transmits the broadcast. ++ ․ ++ h. "Work" means the literary and/or artistic work offered under the ++ terms of this License including without limitation any production in the ++ literary, scientific and artistic domain, whatever may be the mode or ++ form of its expression including digital form, such as a book, pamphlet ++ and other writing; a lecture, address, sermon or other work of the same ++ nature; a dramatic or dramatico-musical work; a choreographic work or ++ entertainment in dumb show; a musical composition with or without words; ++ a cinematographic work to which are assimilated works expressed by a ++ process analogous to cinematography; a work of drawing, painting, ++ architecture, sculpture, engraving or lithography; a photographic work ++ to which are assimilated works expressed by a process analogous to ++ photography; a work of applied art; an illustration, map, plan, sketch ++ or three-dimensional work relative to geography, topography, ++ architecture or science; a performance; a broadcast; a phonogram; a ++ compilation of data to the extent it is protected as a copyrightable ++ work; or a work performed by a variety or circus performer to the extent ++ it is not otherwise considered a literary or artistic work. ++ ․ ++ i. "You" means an individual or entity exercising rights under this ++ License who has not previously violated the terms of this License with ++ respect to the Work, or who has received express permission from the ++ Licensor to exercise rights under this License despite a previous ++ violation. ++ ․ ++ j. "Publicly Perform" means to perform public recitations of the Work ++ and to communicate to the public those public recitations, by any means ++ or process, including by wire or wireless means or public digital ++ performances; to make available to the public Works in such a way that ++ members of the public may access these Works from a place and at a place ++ individually chosen by them; to perform the Work to the public by any ++ means or process and the communication to the public of the performances ++ of the Work, including by public digital performance; to broadcast and ++ rebroadcast the Work by any means including signs, sounds or images. ++ ․ ++ k. "Reproduce" means to make copies of the Work by any means including ++ without limitation by sound or visual recordings and the right of ++ fixation and reproducing fixations of the Work, including storage of a ++ protected performance or phonogram in digital form or other electronic ++ medium. ++ ․ ++ 2. Fair Dealing Rights. Nothing in this License is intended to reduce, ++ limit, or restrict any uses free from copyright or rights arising from ++ limitations or exceptions that are provided for in connection with the ++ copyright protection under copyright law or other applicable laws. ++ ․ ++ 3. License Grant. Subject to the terms and conditions of this License, ++ Licensor hereby grants You a worldwide, royalty-free, non-exclusive, ++ perpetual (for the duration of the applicable copyright) license to ++ exercise the rights in the Work as stated below: ++ ․ ++ a. to Reproduce the Work, to incorporate the Work into one or more ++ Collections, and to Reproduce the Work as incorporated in the ++ Collections; ++ ․ ++ b. to create and Reproduce Adaptations provided that any such ++ Adaptation, including any translation in any medium, takes reasonable ++ steps to clearly label, demarcate or otherwise identify that changes ++ were made to the original Work. For example, a translation could be ++ marked "The original work was translated from English to Spanish," or a ++ modification could indicate "The original work has been modified."; ++ ․ ++ c. to Distribute and Publicly Perform the Work including as incorporated ++ in Collections; and, ++ ․ ++ d. to Distribute and Publicly Perform Adaptations. ++ ․ ++ e. For the avoidance of doubt: ++ ․ ++ i. Non-waivable Compulsory License Schemes. In those jurisdictions in ++ which the right to collect royalties through any statutory or compulsory ++ licensing scheme cannot be waived, the Licensor reserves the exclusive ++ right to collect such royalties for any exercise by You of the rights ++ granted under this License; ++ ․ ++ ii. Waivable Compulsory License Schemes. In those jurisdictions in which ++ the right to collect royalties through any statutory or compulsory ++ licensing scheme can be waived, the Licensor waives the exclusive right ++ to collect such royalties for any exercise by You of the rights granted ++ under this License; and, ++ ․ ++ iii. Voluntary License Schemes. The Licensor waives the right to collect ++ royalties, whether individually or, in the event that the Licensor is a ++ member of a collecting society that administers voluntary licensing ++ schemes, via that society, from any exercise by You of the rights ++ granted under this License. ++ ․ ++ The above rights may be exercised in all media and formats whether now ++ known or hereafter devised. The above rights include the right to make ++ such modifications as are technically necessary to exercise the rights ++ in other media and formats. Subject to Section 8(f), all rights not ++ expressly granted by Licensor are hereby reserved. ++ ․ ++ 4. Restrictions. The license granted in Section 3 above is expressly ++ made subject to and limited by the following restrictions: ++ ․ ++ a. You may Distribute or Publicly Perform the Work only under the terms ++ of this License. You must include a copy of, or the Uniform Resource ++ Identifier (URI) for, this License with every copy of the Work You ++ Distribute or Publicly Perform. You may not offer or impose any terms on ++ the Work that restrict the terms of this License or the ability of the ++ recipient of the Work to exercise the rights granted to that recipient ++ under the terms of the License. You may not sublicense the Work. You ++ must keep intact all notices that refer to this License and to the ++ disclaimer of warranties with every copy of the Work You Distribute or ++ Publicly Perform. When You Distribute or Publicly Perform the Work, You ++ may not impose any effective technological measures on the Work that ++ restrict the ability of a recipient of the Work from You to exercise the ++ rights granted to that recipient under the terms of the License. This ++ Section 4(a) applies to the Work as incorporated in a Collection, but ++ this does not require the Collection apart from the Work itself to be ++ made subject to the terms of this License. If You create a Collection, ++ upon notice from any Licensor You must, to the extent practicable, ++ remove from the Collection any credit as required by Section 4(c), as ++ requested. If You create an Adaptation, upon notice from any Licensor ++ You must, to the extent practicable, remove from the Adaptation any ++ credit as required by Section 4(c), as requested. ++ ․ ++ b. You may Distribute or Publicly Perform an Adaptation only under the ++ terms of: (i) this License; (ii) a later version of this License with ++ the same License Elements as this License; (iii) a Creative Commons ++ jurisdiction license (either this or a later license version) that ++ contains the same License Elements as this License (e.g., ++ Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible ++ License. If you license the Adaptation under one of the licenses ++ mentioned in (iv), you must comply with the terms of that license. If ++ you license the Adaptation under the terms of any of the licenses ++ mentioned in (i), (ii) or (iii) (the "Applicable License"), you must ++ comply with the terms of the Applicable License generally and the ++ following provisions: (I) You must include a copy of, or the URI for, ++ the Applicable License with every copy of each Adaptation You Distribute ++ or Publicly Perform; (II) You may not offer or impose any terms on the ++ Adaptation that restrict the terms of the Applicable License or the ++ ability of the recipient of the Adaptation to exercise the rights ++ granted to that recipient under the terms of the Applicable License; ++ (III) You must keep intact all notices that refer to the Applicable ++ License and to the disclaimer of warranties with every copy of the Work ++ as included in the Adaptation You Distribute or Publicly Perform; (IV) ++ when You Distribute or Publicly Perform the Adaptation, You may not ++ impose any effective technological measures on the Adaptation that ++ restrict the ability of a recipient of the Adaptation from You to ++ exercise the rights granted to that recipient under the terms of the ++ Applicable License. This Section 4(b) applies to the Adaptation as ++ incorporated in a Collection, but this does not require the Collection ++ apart from the Adaptation itself to be made subject to the terms of the ++ Applicable License. ++ ․ ++ c. If You Distribute, or Publicly Perform the Work or any Adaptations or ++ Collections, You must, unless a request has been made pursuant to ++ Section 4(a), keep intact all copyright notices for the Work and ++ provide, reasonable to the medium or means You are utilizing: (i) the ++ name of the Original Author (or pseudonym, if applicable) if supplied, ++ and/or if the Original Author and/or Licensor designate another party or ++ parties (e.g., a sponsor institute, publishing entity, journal) for ++ attribution ("Attribution Parties") in Licensor's copyright notice, ++ terms of service or by other reasonable means, the name of such party or ++ parties; (ii) the title of the Work if supplied; (iii) to the extent ++ reasonably practicable, the URI, if any, that Licensor specifies to be ++ associated with the Work, unless such URI does not refer to the ++ copyright notice or licensing information for the Work; and (iv) , ++ consistent with Ssection 3(b), in the case of an Adaptation, a credit ++ identifying the use of the Work in the Adaptation (e.g., "French ++ translation of the Work by Original Author," or "Screenplay based on ++ original Work by Original Author"). The credit required by this Section ++ 4(c) may be implemented in any reasonable manner; provided, however, ++ that in the case of a Adaptation or Collection, at a minimum such credit ++ will appear, if a credit for all contributing authors of the Adaptation ++ or Collection appears, then as part of these credits and in a manner at ++ least as prominent as the credits for the other contributing authors. ++ For the avoidance of doubt, You may only use the credit required by this ++ Section for the purpose of attribution in the manner set out above and, ++ by exercising Your rights under this License, You may not implicitly or ++ explicitly assert or imply any connection with, sponsorship or ++ endorsement by the Original Author, Licensor and/or Attribution Parties, ++ as appropriate, of You or Your use of the Work, without the separate, ++ express prior written permission of the Original Author, Licensor and/or ++ Attribution Parties. ++ ․ ++ d. Except as otherwise agreed in writing by the Licensor or as may be ++ otherwise permitted by applicable law, if You Reproduce, Distribute or ++ Publicly Perform the Work either by itself or as part of any Adaptations ++ or Collections, You must not distort, mutilate, modify or take other ++ derogatory action in relation to the Work which would be prejudicial to ++ the Original Author's honor or reputation. Licensor agrees that in those ++ jurisdictions (e.g. Japan), in which any exercise of the right granted ++ in Section 3(b) of this License (the right to make Adaptations) would be ++ deemed to be a distortion, mutilation, modification or other derogatory ++ action prejudicial to the Original Author's honor and reputation, the ++ Licensor will waive or not assert, as appropriate, this Section, to the ++ fullest extent permitted by the applicable national law, to enable You ++ to reasonably exercise Your right under Section 3(b) of this License ++ (right to make Adaptations) but not otherwise. ++ ․ ++ 5. Representations, Warranties and Disclaimer ++ ․ ++ UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR ++ OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY ++ KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, ++ INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, ++ FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF ++ LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, ++ WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE ++ EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. ++ ․ ++ 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE ++ LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ++ ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ++ ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS ++ BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ++ ․ ++ 7. Termination ++ ․ ++ a. This License and the rights granted hereunder will terminate ++ automatically upon any breach by You of the terms of this License. ++ Individuals or entities who have received Adaptations or Collections ++ from You under this License, however, will not have their licenses ++ terminated provided such individuals or entities remain in full ++ compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will ++ survive any termination of this License. ++ ․ ++ b. Subject to the above terms and conditions, the license granted here ++ is perpetual (for the duration of the applicable copyright in the Work). ++ Notwithstanding the above, Licensor reserves the right to release the ++ Work under different license terms or to stop distributing the Work at ++ any time; provided, however that any such election will not serve to ++ withdraw this License (or any other license that has been, or is ++ required to be, granted under the terms of this License), and this ++ License will continue in full force and effect unless terminated as ++ stated above. ++ ․ ++ 8. Miscellaneous ++ ․ ++ a. Each time You Distribute or Publicly Perform the Work or a ++ Collection, the Licensor offers to the recipient a license to the Work ++ on the same terms and conditions as the license granted to You under ++ this License. ++ ․ ++ b. Each time You Distribute or Publicly Perform an Adaptation, Licensor ++ offers to the recipient a license to the original Work on the same terms ++ and conditions as the license granted to You under this License. ++ ․ ++ c. If any provision of this License is invalid or unenforceable under ++ applicable law, it shall not affect the validity or enforceability of ++ the remainder of the terms of this License, and without further action ++ by the parties to this agreement, such provision shall be reformed to ++ the minimum extent necessary to make such provision valid and ++ enforceable. ++ ․ ++ d. No term or provision of this License shall be deemed waived and no ++ breach consented to unless such waiver or consent shall be in writing ++ and signed by the party to be charged with such waiver or consent. ++ ․ ++ e. This License constitutes the entire agreement between the parties ++ with respect to the Work licensed here. There are no understandings, ++ agreements or representations with respect to the Work not specified ++ here. Licensor shall not be bound by any additional provisions that may ++ appear in any communication from You. This License may not be modified ++ without the mutual written agreement of the Licensor and You. ++ ․ ++ f. The rights granted under, and the subject matter referenced, in this ++ License were drafted utilizing the terminology of the Berne Convention ++ for the Protection of Literary and Artistic Works (as amended on ++ September 28, 1979), the Rome Convention of 1961, the WIPO Copyright ++ Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and ++ the Universal Copyright Convention (as revised on July 24, 1971). These ++ rights and subject matter take effect in the relevant jurisdiction in ++ which the License terms are sought to be enforced according to the ++ corresponding provisions of the implementation of those treaty ++ provisions in the applicable national law. If the standard suite of ++ rights granted under applicable copyright law includes additional rights ++ not granted under this License, such additional rights are deemed to be ++ included in the License; this License is not intended to restrict the ++ license of any rights under applicable law. ++ ․ ++ ․ ++ Creative Commons Notice ++ ․ ++ Creative Commons is not a party to this License, and makes no warranty ++ whatsoever in connection with the Work. Creative Commons will not be ++ liable to You or any party on any legal theory for any damages ++ whatsoever, including without limitation any general, special, ++ incidental or consequential damages arising in connection to this ++ license. Notwithstanding the foregoing two (2) sentences, if Creative ++ Commons has expressly identified itself as the Licensor hereunder, it ++ shall have all rights and obligations of Licensor. ++ ․ ++ Except for the limited purpose of indicating to the public that the Work ++ is licensed under the CCPL, Creative Commons does not authorize the use ++ by either party of the trademark "Creative Commons" or any related ++ trademark or logo of Creative Commons without the prior written consent ++ of Creative Commons. Any permitted use will be in compliance with ++ Creative Commons' then-current trademark usage guidelines, as may be ++ published on its website or otherwise made available upon request from ++ time to time. For the avoidance of doubt, this trademark restriction ++ does not form part of the License. ++ ․ ++ Creative Commons may be contacted at http://creativecommons.org/. ++ ++License: BSD-3-clause ++ Redistribution and use in source and binary forms, with or without ++ modification, are permitted provided that the following conditions ++ are met: ++ . ++ 1. Redistributions of source code must retain the above ++ copyright notice, this list of conditions and the following ++ disclaimer. ++ . ++ 2. Redistributions in binary form must reproduce the above ++ copyright notice, this list of conditions and the following ++ disclaimer in the documentation and/or other materials ++ provided with the distribution. ++ . ++ 3. Neither the name of the copyright holder nor the names of ++ its contributors may be used to endorse or promote products ++ derived from this software without specific prior written ++ permission. ++ . ++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ++ FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ++ COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, ++ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, ++ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED ++ OF THE POSSIBILITY OF SUCH DAMAGE. diff --cc debian/gbp.conf index 000000000,000000000..023aff39f new file mode 100644 --- /dev/null +++ b/debian/gbp.conf @@@ -1,0 -1,0 +1,3 @@@ ++[DEFAULT] ++debian-branch = debian/unstable ++pristine-tar = True diff --cc debian/lib-systemd/system-sleep/ceph index 000000000,000000000..cf62f8321 new file mode 100755 --- /dev/null +++ b/debian/lib-systemd/system-sleep/ceph @@@ -1,0 -1,0 +1,12 @@@ ++#!/bin/sh ++ ++#/lib/systemd/system-sleep/ceph ++ ++case $1 in ++pre) ++ /bin/systemctl stop ceph ++;; ++post) ++ /bin/systemctl start ceph ++;; ++esac diff --cc debian/lib-systemd/system/ceph-create-keys.service index 000000000,000000000..4e29bc1e9 new file mode 100644 --- /dev/null +++ b/debian/lib-systemd/system/ceph-create-keys.service @@@ -1,0 -1,0 +1,9 @@@ ++[Unit] ++Description=Create Ceph client.admin key when possible ++PartOf=ceph-mon.service ++ ++[Service] ++Environment=CLUSTER=ceph ++Environment=CONFIG=/etc/ceph/ceph.conf ++EnvironmentFile=-/etc/default/ceph ++ExecStart=/usr/sbin/ceph-create-keys --cluster ${CLUSTER} --id %H diff --cc debian/lib-systemd/system/ceph-mds.service index 000000000,000000000..86ff057f1 new file mode 100644 --- /dev/null +++ b/debian/lib-systemd/system/ceph-mds.service @@@ -1,0 -1,0 +1,16 @@@ ++[Unit] ++Description=Ceph metadata server daemon (MDS) ++Documentation=man:ceph-mds ++After=network-online.target nss-lookup.target ++Wants=network-online.target nss-lookup.target ++PartOf=ceph.target ++ ++[Service] ++LimitNOFILE=1048576 ++LimitNPROC=1048576 ++EnvironmentFile=-/etc/default/ceph ++Environment=CLUSTER=ceph ++ExecStart=/usr/bin/ceph-mds -f --cluster ${CLUSTER} --id %H --setuser ceph --setgroup ceph ++ ++[Install] ++WantedBy=multi-user.target diff --cc debian/lib-systemd/system/ceph-mon.service index 000000000,000000000..d89c74a64 new file mode 100644 --- /dev/null +++ b/debian/lib-systemd/system/ceph-mon.service @@@ -1,0 -1,0 +1,22 @@@ ++[Unit] ++Description=Ceph cluster monitor daemon ++Documentation=man:ceph-mon ++ ++After=network-online.target local-fs.target ceph-create-keys.service ++Wants=network-online.target local-fs.target ceph-create-keys.service ++ ++PartOf=ceph.target ++ ++[Service] ++LimitNOFILE=1048576 ++LimitNPROC=1048576 ++EnvironmentFile=-/etc/default/ceph ++Environment=CLUSTER=ceph ++ExecStart=/usr/bin/ceph-mon -f --cluster ${CLUSTER} --id %H --setuser ceph --setgroup ceph ++ExecReload=/bin/kill -HUP $MAINPID ++Restart=on-failure ++RestartSec=30 ++TasksMax=infinity ++ ++[Install] ++WantedBy=multi-user.target diff --cc debian/lib-systemd/system/ceph-osd@.service index 000000000,000000000..adfa6a02f new file mode 100644 --- /dev/null +++ b/debian/lib-systemd/system/ceph-osd@.service @@@ -1,0 -1,0 +1,22 @@@ ++[Unit] ++Description=Ceph object storage daemon (OSD) ++Documentation=man:ceph-osd ++After=network-online.target ++Wants=network-online.target ++PartOf=ceph.service ++RequiresMountsFor=/var/lib/ceph/osd/ceph-%i ++ ++[Service] ++Environment=CLUSTER=ceph ++Environment=CONFIG=/etc/ceph/ceph.conf ++EnvironmentFile=-/etc/default/ceph ++ExecStartPre=-/bin/sh -c '${osd_prestart_sh}' -- %i ++ExecStartPre=/usr/lib/ceph/ceph-osd-prestart.sh --id %i --cluster ${CLUSTER} ++ExecStart=/usr/bin/ceph-osd --id %i --foreground --cluster ${CLUSTER} -c ${CONFIG} ++ExecStopPost=-/bin/sh -c '${osd_poststop_sh}' -- %i ++LimitNOFILE=327680 ++Restart=on-failure ++RestartSec=30 ++ ++[Install] ++WantedBy=multi-user.target diff --cc debian/libcephfs-dev.install index fbc1e4b95,000000000..a23c5b057 mode 100644,000000..100644 --- a/debian/libcephfs-dev.install +++ b/debian/libcephfs-dev.install @@@ -1,3 -1,0 +1,2 @@@ - usr/include/cephfs/ceph_ll_client.h - usr/include/cephfs/libcephfs.h - usr/lib/libcephfs.so ++usr/include/cephfs/*.h ++usr/lib/*/libcephfs.so diff --cc debian/libcephfs-jni.install index 072b99033,000000000..15cb91d90 mode 100644,000000..100644 --- a/debian/libcephfs-jni.install +++ b/debian/libcephfs-jni.install @@@ -1,1 -1,0 +1,1 @@@ - usr/lib/libcephfs_jni.so* usr/lib/jni ++usr/lib/*/libcephfs_jni.so* usr/lib/jni diff --cc debian/libcephfs-jni.lintian-overrides index 000000000,000000000..93a0dacaf new file mode 100644 --- /dev/null +++ b/debian/libcephfs-jni.lintian-overrides @@@ -1,0 -1,0 +1,1 @@@ ++binary-or-shlib-defines-rpath usr/lib/jni/* diff --cc debian/libcephfs2.install index b7c0eb058,000000000..f09e93ca5 mode 100644,000000..100644 --- a/debian/libcephfs2.install +++ b/debian/libcephfs2.install @@@ -1,1 -1,0 +1,1 @@@ - usr/lib/libcephfs.so.* ++usr/lib/*/libcephfs.so.* diff --cc debian/libcephfs2.lintian-overrides index 000000000,000000000..d4041ca8e new file mode 100644 --- /dev/null +++ b/debian/libcephfs2.lintian-overrides @@@ -1,0 -1,0 +1,2 @@@ ++# False-positives: ++spelling-error-in-binary * tEH the diff --cc debian/libcephfs2.symbols index 000000000,000000000..b94b81dc4 new file mode 100644 --- /dev/null +++ b/debian/libcephfs2.symbols @@@ -1,0 -1,0 +1,186 @@@ ++libcephfs.so.2 libcephfs2 #MINVER# ++ (regex|c++)"^_.*" 12.0.3 ++ ceph_abort_conn@Base 14.2.0 ++ ceph_buffer_free@Base 12.0.3 ++ ceph_chdir@Base 12.0.3 ++ ceph_chmod@Base 12.0.3 ++ ceph_chown@Base 12.0.3 ++ ceph_close@Base 12.0.3 ++ ceph_closedir@Base 12.0.3 ++ ceph_conf_get@Base 12.0.3 ++ ceph_conf_parse_argv@Base 12.0.3 ++ ceph_conf_parse_env@Base 12.0.3 ++ ceph_conf_read_file@Base 12.0.3 ++ ceph_conf_set@Base 12.0.3 ++ ceph_create@Base 12.0.3 ++ ceph_create_from_rados@Base 12.0.3 ++ ceph_create_with_context@Base 12.0.3 ++ ceph_debug_get_fd_caps@Base 12.0.3 ++ ceph_debug_get_file_caps@Base 12.0.3 ++ ceph_fallocate@Base 12.0.3 ++ ceph_fchmod@Base 12.0.3 ++ ceph_fchown@Base 12.0.3 ++ ceph_fgetxattr@Base 12.0.3 ++ ceph_finish_reclaim@Base 14.2.0 ++ ceph_flistxattr@Base 12.0.3 ++ ceph_flock@Base 12.0.3 ++ ceph_fremovexattr@Base 12.0.3 ++ ceph_fsetattrx@Base 12.0.3 ++ ceph_fsetxattr@Base 12.0.3 ++ ceph_fstat@Base 14.2.0 ++ ceph_fstatx@Base 12.0.3 ++ ceph_fsync@Base 12.0.3 ++ ceph_ftruncate@Base 12.0.3 ++ ceph_futime@Base 14.2.0 ++ ceph_futimens@Base 14.2.0 ++ ceph_futimes@Base 14.2.0 ++ ceph_get_cap_return_timeout@Base 13.2.0 ++ ceph_get_default_data_pool_name@Base 12.1.4 ++ ceph_get_file_extent_osds@Base 12.0.3 ++ ceph_get_file_layout@Base 12.0.3 ++ ceph_get_file_object_size@Base 12.0.3 ++ ceph_get_file_pool@Base 12.0.3 ++ ceph_get_file_pool_name@Base 12.0.3 ++ ceph_get_file_replication@Base 12.0.3 ++ ceph_get_file_stripe_address@Base 12.0.3 ++ ceph_get_file_stripe_count@Base 12.0.3 ++ ceph_get_file_stripe_unit@Base 12.0.3 ++ ceph_get_fs_cid@Base 14.2.0 ++ ceph_get_instance_id@Base 14.2.0 ++ ceph_get_local_osd@Base 12.0.3 ++ ceph_get_mount_context@Base 12.0.3 ++ ceph_get_osd_addr@Base 12.0.3 ++ ceph_get_osd_crush_location@Base 12.0.3 ++ ceph_get_path_layout@Base 12.0.3 ++ ceph_get_path_object_size@Base 12.0.3 ++ ceph_get_path_pool@Base 12.0.3 ++ ceph_get_path_pool_name@Base 12.0.3 ++ ceph_get_path_replication@Base 12.0.3 ++ ceph_get_path_stripe_count@Base 12.0.3 ++ ceph_get_path_stripe_unit@Base 12.0.3 ++ ceph_get_pool_id@Base 12.0.3 ++ ceph_get_pool_name@Base 12.0.3 ++ ceph_get_pool_replication@Base 12.0.3 ++ ceph_get_stripe_unit_granularity@Base 12.0.3 ++ ceph_getcwd@Base 12.0.3 ++ ceph_getdents@Base 12.0.3 ++ ceph_getdnames@Base 12.0.3 ++ ceph_getxattr@Base 12.0.3 ++ ceph_init@Base 12.0.3 ++ ceph_is_mounted@Base 12.0.3 ++ ceph_lazyio@Base 14.2.0 ++ ceph_lazyio_propagate@Base 14.2.15 ++ ceph_lazyio_synchronize@Base 14.2.15 ++ ceph_lchown@Base 12.0.3 ++ ceph_lgetxattr@Base 12.0.3 ++ ceph_link@Base 12.0.3 ++ ceph_listxattr@Base 12.0.3 ++ ceph_ll_close@Base 12.0.3 ++ ceph_ll_commit_blocks@Base 12.0.3 ++ ceph_ll_create@Base 12.0.3 ++ ceph_ll_delegation@Base 13.2.0 ++ ceph_ll_fallocate@Base 14.2.0 ++ ceph_ll_file_layout@Base 12.0.3 ++ ceph_ll_forget@Base 12.0.3 ++ ceph_ll_fsync@Base 12.0.3 ++ ceph_ll_get_inode@Base 12.0.3 ++ ceph_ll_get_internal_offset@Base 12.0.3 ++ ceph_ll_get_stripe_osd@Base 12.0.3 ++ ceph_ll_getattr@Base 12.0.3 ++ ceph_ll_getlk@Base 12.0.3 ++ ceph_ll_getxattr@Base 12.0.3 ++ ceph_ll_lazyio@Base 14.2.0 ++ ceph_ll_link@Base 12.0.3 ++ ceph_ll_listxattr@Base 12.0.3 ++ ceph_ll_lookup@Base 12.0.3 ++ ceph_ll_lookup_inode@Base 12.0.3 ++ ceph_ll_lookup_root@Base 12.0.3 ++ ceph_ll_lseek@Base 12.0.3 ++ ceph_ll_mkdir@Base 12.0.3 ++ ceph_ll_mknod@Base 12.0.3 ++ ceph_ll_num_osds@Base 12.0.3 ++ ceph_ll_open@Base 12.0.3 ++ ceph_ll_opendir@Base 12.0.3 ++ ceph_ll_osdaddr@Base 12.0.3 ++ ceph_ll_put@Base 12.0.3 ++ ceph_ll_read@Base 12.0.3 ++ ceph_ll_read_block@Base 12.0.3 ++ ceph_ll_readlink@Base 12.0.3 ++ ceph_ll_readv@Base 12.0.3 ++ ceph_ll_register_callbacks@Base 14.2.15 ++ ceph_ll_releasedir@Base 12.0.3 ++ ceph_ll_removexattr@Base 12.0.3 ++ ceph_ll_rename@Base 12.0.3 ++ ceph_ll_rmdir@Base 12.0.3 ++ ceph_ll_setattr@Base 12.0.3 ++ ceph_ll_setlk@Base 12.0.3 ++ ceph_ll_setxattr@Base 12.0.3 ++ ceph_ll_snap_seq@Base 12.0.3 ++ ceph_ll_statfs@Base 12.0.3 ++ ceph_ll_stripe_unit@Base 12.0.3 ++ ceph_ll_symlink@Base 12.0.3 ++ ceph_ll_sync_inode@Base 13.2.0 ++ ceph_ll_unlink@Base 12.0.3 ++ ceph_ll_walk@Base 12.0.3 ++ ceph_ll_write@Base 12.0.3 ++ ceph_ll_write_block@Base 12.0.3 ++ ceph_ll_writev@Base 12.0.3 ++ ceph_llistxattr@Base 12.0.3 ++ ceph_localize_reads@Base 12.0.3 ++ ceph_lremovexattr@Base 12.0.3 ++ ceph_lseek@Base 12.0.3 ++ ceph_lsetxattr@Base 12.0.3 ++ ceph_lstat@Base 14.2.0 ++ ceph_lutimes@Base 14.2.0 ++ ceph_mds_command@Base 12.0.3 ++ ceph_mkdir@Base 12.0.3 ++ ceph_mkdirs@Base 12.0.3 ++ ceph_mknod@Base 12.0.3 ++ ceph_mount@Base 12.0.3 ++ ceph_mount_perms@Base 12.0.3 ++ ceph_mount_perms_set@Base 13.2.0 ++ ceph_open@Base 12.0.3 ++ ceph_open_layout@Base 12.0.3 ++ ceph_opendir@Base 12.0.3 ++ ceph_preadv@Base 12.0.3 ++ ceph_pwritev@Base 12.0.3 ++ ceph_read@Base 12.0.3 ++ ceph_readdir@Base 12.0.3 ++ ceph_readdir_r@Base 12.0.3 ++ ceph_readdirplus_r@Base 12.0.3 ++ ceph_readlink@Base 12.0.3 ++ ceph_release@Base 12.0.3 ++ ceph_removexattr@Base 12.0.3 ++ ceph_rename@Base 12.0.3 ++ ceph_rewinddir@Base 12.0.3 ++ ceph_rmdir@Base 12.0.3 ++ ceph_seekdir@Base 12.0.3 ++ ceph_select_filesystem@Base 14.2.0 ++ ceph_set_default_file_replication@Base 12.0.3 ++ ceph_set_default_file_stripe_count@Base 12.0.3 ++ ceph_set_default_file_stripe_unit@Base 12.0.3 ++ ceph_set_default_object_size@Base 12.0.3 ++ ceph_set_default_preferred_pg@Base 12.0.3 ++ ceph_set_deleg_timeout@Base 13.2.0 ++ ceph_set_session_timeout@Base 14.2.0 ++ ceph_set_uuid@Base 14.2.0 ++ ceph_setattrx@Base 12.0.3 ++ ceph_setxattr@Base 12.0.3 ++ ceph_shutdown@Base 12.0.3 ++ ceph_start_reclaim@Base 14.2.0 ++ ceph_stat@Base 14.2.0 ++ ceph_statfs@Base 12.0.3 ++ ceph_statx@Base 12.0.3 ++ ceph_symlink@Base 12.0.3 ++ ceph_sync_fs@Base 12.0.3 ++ ceph_telldir@Base 12.0.3 ++ ceph_truncate@Base 12.0.3 ++ ceph_umask@Base 14.2.0 ++ ceph_unlink@Base 12.0.3 ++ ceph_unmount@Base 12.0.3 ++ ceph_userperm_destroy@Base 12.0.3 ++ ceph_userperm_new@Base 12.0.3 ++ ceph_utime@Base 12.0.3 ++ ceph_utimes@Base 14.2.0 ++ ceph_version@Base 12.0.3 ++ ceph_write@Base 12.0.3 diff --cc debian/librados-dev.install index 75d5ea7de,000000000..edaa359fc mode 100644,000000..100644 --- a/debian/librados-dev.install +++ b/debian/librados-dev.install @@@ -1,6 -1,0 +1,5 @@@ +usr/bin/librados-config +usr/include/rados/librados.h +usr/include/rados/rados_types.h - usr/lib/librados.so - usr/lib/librados_tp.so ++usr/lib/*/librados.so +usr/share/man/man8/librados-config.8 diff --cc debian/librados2.install index 816e55fce,000000000..2020e29c4 mode 100644,000000..100644 --- a/debian/librados2.install +++ b/debian/librados2.install @@@ -1,3 -1,0 +1,2 @@@ - usr/lib/ceph/libceph-common.so.* - usr/lib/librados.so.* - usr/lib/librados_tp.so.* ++usr/lib/*/ceph/libceph-common.so* ++usr/lib/*/librados.so.* diff --cc debian/librados2.lintian-overrides index 000000000,000000000..d4041ca8e new file mode 100644 --- /dev/null +++ b/debian/librados2.lintian-overrides @@@ -1,0 -1,0 +1,2 @@@ ++# False-positives: ++spelling-error-in-binary * tEH the diff --cc debian/librados2.symbols index 000000000,000000000..6507d9090 new file mode 100644 --- /dev/null +++ b/debian/librados2.symbols @@@ -1,0 -1,0 +1,423 @@@ ++libceph-common.so.0 librados2 #MINVER# ++ (regex|c++)"^_.*" 12.0.3 ++ (regex)"^ceph_ver__[0-9a-f]{40}@Base$" 12.0.3 ++ MDS_GID_NONE@Base 12.0.3 ++ XXH32@Base 12.0.3 ++ XXH32_canonicalFromHash@Base 12.0.3 ++ XXH32_createState@Base 12.0.3 ++ XXH32_digest@Base 12.0.3 ++ XXH32_freeState@Base 12.0.3 ++ XXH32_hashFromCanonical@Base 12.0.3 ++ XXH32_reset@Base 12.0.3 ++ XXH32_update@Base 12.0.3 ++ XXH64@Base 12.0.3 ++ XXH64_canonicalFromHash@Base 12.0.3 ++ XXH64_createState@Base 12.0.3 ++ XXH64_digest@Base 12.0.3 ++ XXH64_freeState@Base 12.0.3 ++ XXH64_hashFromCanonical@Base 12.0.3 ++ XXH64_reset@Base 12.0.3 ++ XXH64_update@Base 12.0.3 ++ XXH_versionNumber@Base 12.0.3 ++ boost_asio_detail_posix_thread_function@Base 12.0.3 ++ (arch=amd64 i386)ceph_arch_intel_aesni@Base 12.0.3 ++ (arch=amd64 i386)ceph_arch_intel_pclmul@Base 12.0.3 ++ (arch=amd64 i386)ceph_arch_intel_probe@Base 12.0.3 ++ (arch=amd64 i386)ceph_arch_intel_sse2@Base 12.0.3 ++ (arch=amd64 i386)ceph_arch_intel_sse3@Base 12.0.3 ++ (arch=amd64 i386)ceph_arch_intel_sse41@Base 12.0.3 ++ (arch=amd64 i386)ceph_arch_intel_sse42@Base 12.0.3 ++ (arch=amd64 i386)ceph_arch_intel_ssse3@Base 12.0.3 ++ (arch=arm64 armhf)ceph_arch_aarch64_crc32@Base 12.0.3 ++ (arch=arm64 armhf)ceph_arch_arm_probe@Base 12.0.3 ++ (arch=ppc64el)ceph_arch_ppc_crc32@Base 12.0.3 ++ (arch=ppc64el)ceph_arch_ppc_probe@Base 12.0.3a ++ (arch=arm64 armhf)ceph_arch_neon@Base 12.0.3 ++ ceph_arch_probe@Base 12.0.3 ++ ceph_arch_probed@Base 12.0.3 ++ ceph_armor@Base 12.0.3 ++ ceph_armor_line_break@Base 12.0.3 ++ ceph_choose_crc32@Base 12.1.4 ++ (arch=arm64)ceph_crc32c_aarch64@Base 12.0.3 ++ ceph_crc32c_func@Base 12.0.3 ++ (arch=amd64)ceph_crc32c_intel_baseline@Base 12.0.3 ++ (arch=amd64 i386)ceph_crc32c_intel_fast@Base 12.0.3 ++ (arch=amd64 i386)ceph_crc32c_intel_fast_exists@Base 12.0.3 ++ (arch=ppc64el)ceph_crc32c_ppc@Base 12.0.3 ++ ceph_crc32c_sctp@Base 12.0.3 ++ ceph_crc32c_zeros@Base 12.1.4 ++ ceph_options@Base 12.1.4 ++ ceph_os_fgetxattr@Base 12.0.3 ++ ceph_os_flistxattr@Base 12.0.3 ++ ceph_os_fremovexattr@Base 12.0.3 ++ ceph_os_fsetxattr@Base 12.0.3 ++ ceph_os_getxattr@Base 12.0.3 ++ ceph_os_listxattr@Base 12.0.3 ++ ceph_os_removexattr@Base 12.0.3 ++ ceph_os_setxattr@Base 12.0.3 ++ ceph_unarmor@Base 12.0.3 ++ check_for_control_characters@Base 12.0.3 ++ check_for_control_characters_cstr@Base 12.0.3 ++ check_utf8@Base 12.0.3 ++ check_utf8_cstr@Base 12.0.3 ++ code_environment_to_str@Base 12.0.3 ++ (arch=amd64)crc32_iscsi_00@Base 12.0.3 ++ (arch=amd64)crc32_iscsi_00_slver@Base 12.0.3 ++ (arch=amd64)crc32_iscsi_00_slver_00020014@Base 12.0.3 ++ (arch=amd64)crc32_iscsi_zero_00@Base 12.0.3 ++ (arch=amd64)crc32_iscsi_zero_00_slver@Base 12.0.3 ++ (arch=amd64)crc32_iscsi_zero_00_slver_00020014@Base 12.0.3 ++ (arch=amd64)crc32_table_iscsi_base@Base 12.0.3 ++ (arch=ppc64el)crc_zero@Base 12.1.4 ++ crush_add_bucket@Base 12.0.3 ++ crush_add_list_bucket_item@Base 12.0.3 ++ crush_add_rule@Base 12.0.3 ++ crush_add_straw2_bucket_item@Base 12.0.3 ++ crush_add_straw_bucket_item@Base 12.0.3 ++ crush_add_tree_bucket_item@Base 12.0.3 ++ crush_add_uniform_bucket_item@Base 12.0.3 ++ crush_addition_is_unsafe@Base 12.0.3 ++ crush_adjust_list_bucket_item_weight@Base 12.0.3 ++ crush_adjust_straw2_bucket_item_weight@Base 12.0.3 ++ crush_adjust_straw_bucket_item_weight@Base 12.0.3 ++ crush_adjust_tree_bucket_item_weight@Base 12.0.3 ++ crush_adjust_uniform_bucket_item_weight@Base 12.0.3 ++ crush_bucket_add_item@Base 12.0.3 ++ crush_bucket_adjust_item_weight@Base 12.0.3 ++ crush_bucket_alg_name@Base 12.0.3 ++ crush_bucket_remove_item@Base 12.0.3 ++ crush_calc_straw@Base 12.0.3 ++ crush_create@Base 12.0.3 ++ crush_destroy@Base 12.0.3 ++ crush_destroy_bucket@Base 12.0.3 ++ crush_destroy_bucket_list@Base 12.0.3 ++ crush_destroy_bucket_straw2@Base 12.0.3 ++ crush_destroy_bucket_straw@Base 12.0.3 ++ crush_destroy_bucket_tree@Base 12.0.3 ++ crush_destroy_bucket_uniform@Base 12.0.3 ++ crush_destroy_choose_args@Base 12.0.3 ++ crush_destroy_rule@Base 12.0.3 ++ crush_do_rule@Base 12.0.3 ++ crush_finalize@Base 12.0.3 ++ crush_find_rule@Base 12.0.3 ++ crush_get_bucket_item_weight@Base 12.0.3 ++ crush_get_next_bucket_id@Base 12.0.3 ++ crush_hash32@Base 12.0.3 ++ crush_hash32_2@Base 12.0.3 ++ crush_hash32_3@Base 12.0.3 ++ crush_hash32_4@Base 12.0.3 ++ crush_hash32_5@Base 12.0.3 ++ crush_hash_name@Base 12.0.3 ++ crush_init_workspace@Base 12.0.3 ++ crush_make_bucket@Base 12.0.3 ++ crush_make_choose_args@Base 12.0.3 ++ crush_make_list_bucket@Base 12.0.3 ++ crush_make_rule@Base 12.0.3 ++ crush_make_straw2_bucket@Base 12.0.3 ++ crush_make_straw_bucket@Base 12.0.3 ++ crush_make_tree_bucket@Base 12.0.3 ++ crush_make_uniform_bucket@Base 12.0.3 ++ crush_multiplication_is_unsafe@Base 12.0.3 ++ crush_remove_bucket@Base 12.0.3 ++ crush_remove_list_bucket_item@Base 12.0.3 ++ crush_remove_straw2_bucket_item@Base 12.0.3 ++ crush_remove_straw_bucket_item@Base 12.0.3 ++ crush_remove_tree_bucket_item@Base 12.0.3 ++ crush_remove_uniform_bucket_item@Base 12.0.3 ++ crush_reweight_bucket@Base 12.0.3 ++ crush_rule_set_step@Base 12.0.3 ++ current_maxid@Base 12.0.3 ++ decode_utf8@Base 12.0.3 ++ encode_utf8@Base 12.0.3 ++ g_assert_condition@Base 14.2.0 ++ g_assert_file@Base 14.2.0 ++ g_assert_func@Base 14.2.0 ++ g_assert_line@Base 14.2.0 ++ g_assert_msg@Base 14.2.0 ++ g_assert_thread@Base 14.2.0 ++ g_assert_thread_name@Base 14.2.0 ++ g_ceph_context@Base 12.0.3 ++ g_code_env@Base 12.0.3 ++ g_eio@Base 14.2.0 ++ g_eio_devname@Base 14.2.0 ++ g_eio_error@Base 14.2.0 ++ g_eio_iotype@Base 14.2.0 ++ g_eio_length@Base 14.2.0 ++ g_eio_offset@Base 14.2.0 ++ g_eio_path@Base 14.2.0 ++ g_lockdep@Base 12.0.3 ++ g_process_name@Base 14.2.0 ++ get_linux_version@Base 12.0.3 ++ get_process_name@Base 12.0.3 ++ is_control_character@Base 12.0.3 ++ last_freed_id@Base 12.0.3 ++ mime_decode_from_qp@Base 12.0.3 ++ mime_encode_as_qp@Base 12.0.3 ++ module_has_param@Base 12.0.3 ++ module_load@Base 12.0.3 ++ pem_key@Base 12.0.3 ++ resolve_addrs@Base 12.0.3 ++ reverse_bits@Base 12.1.4 ++ reverse_nibbles@Base 12.1.4 ++ safe_cat@Base 12.0.3 ++ safe_pread@Base 12.0.3 ++ safe_pread_exact@Base 12.0.3 ++ safe_pwrite@Base 12.0.3 ++ safe_read@Base 12.0.3 ++ safe_read_exact@Base 12.0.3 ++ safe_read_file@Base 12.0.3 ++ safe_splice@Base 12.0.3 ++ safe_splice_exact@Base 12.0.3 ++ safe_write@Base 12.0.3 ++ safe_write_file@Base 12.0.3 ++ sctp_crc_c@Base 12.0.3 ++ sctp_crc_tableil8_o32@Base 12.0.3 ++ sctp_crc_tableil8_o40@Base 12.0.3 ++ sctp_crc_tableil8_o48@Base 12.0.3 ++ sctp_crc_tableil8_o56@Base 12.0.3 ++ sctp_crc_tableil8_o64@Base 12.0.3 ++ sctp_crc_tableil8_o72@Base 12.0.3 ++ sctp_crc_tableil8_o80@Base 12.0.3 ++ sctp_crc_tableil8_o88@Base 12.0.3 ++ set_legacy_crush_map@Base 12.0.3 ++ set_optimal_crush_map@Base 12.0.3 ++librados.so.2 librados2 #MINVER# ++ (symver)LIBRADOS_14.2.0 14.2.0 ++ (symver)LIBRADOS_PRIVATE 14.2.0 ++ rados_aio_append@Base 0.72.2 ++ rados_aio_cancel@Base 0.87 ++ rados_aio_cmpext@Base 12.0.3 ++ rados_aio_create_completion@Base 0.72.2 ++ rados_aio_exec@Base 12.0.3 ++ rados_aio_flush@Base 0.72.2 ++ rados_aio_flush_async@Base 0.72.2 ++ rados_aio_get_return_value@Base 0.72.2 ++ rados_aio_get_version@Base 12.0.3 ++ rados_aio_getxattr@Base 12.0.3 ++ rados_aio_getxattrs@Base 12.0.3 ++ rados_aio_ioctx_selfmanaged_snap_create@Base 12.0.3 ++ rados_aio_ioctx_selfmanaged_snap_remove@Base 12.0.3 ++ rados_aio_is_complete@Base 0.72.2 ++ rados_aio_is_complete_and_cb@Base 0.72.2 ++ rados_aio_is_safe@Base 0.72.2 ++ rados_aio_is_safe_and_cb@Base 0.72.2 ++ rados_aio_notify@Base 10.1.0 ++ rados_aio_read@Base 0.72.2 ++ rados_aio_read_op_operate@Base 0.79 ++ rados_aio_release@Base 0.72.2 ++ rados_aio_remove@Base 0.72.2 ++ rados_aio_rmxattr@Base 12.0.3 ++ rados_aio_setxattr@Base 12.0.3 ++ rados_aio_stat@Base 0.72.2 ++ rados_aio_unlock@Base 12.0.3 ++ rados_aio_unwatch@Base 10.1.0 ++ rados_aio_wait_for_complete@Base 0.72.2 ++ rados_aio_wait_for_complete_and_cb@Base 0.72.2 ++ rados_aio_wait_for_safe@Base 0.72.2 ++ rados_aio_wait_for_safe_and_cb@Base 0.72.2 ++ rados_aio_watch2@Base 12.0.3 ++ rados_aio_watch@Base 10.1.0 ++ rados_aio_watch_flush@Base 10.1.0 ++ rados_aio_write@Base 0.72.2 ++ rados_aio_write_full@Base 0.72.2 ++ rados_aio_write_op_operate@Base 0.79 ++ rados_aio_writesame@Base 12.0.3 ++ rados_append@Base 0.72.2 ++ rados_application_enable@Base 12.1.4 ++ rados_application_list@Base 12.1.4 ++ rados_application_metadata_get@Base 12.1.4 ++ rados_application_metadata_list@Base 12.1.4 ++ rados_application_metadata_remove@Base 12.1.4 ++ rados_application_metadata_set@Base 12.1.4 ++ rados_blacklist_add@Base 0.93 ++ rados_break_lock@Base 0.72.2 ++ rados_buffer_free@Base 0.72.2 ++ rados_cache_pin@Base 10.1.0 ++ rados_cache_unpin@Base 10.1.0 ++ rados_cct@Base 0.72.2 ++ rados_checksum@Base 12.0.3 ++ rados_cluster_fsid@Base 0.72.2 ++ rados_cluster_stat@Base 0.72.2 ++ rados_cmpext@Base 12.0.3 ++ rados_conf_get@Base 0.72.2 ++ rados_conf_parse_argv@Base 0.72.2 ++ rados_conf_parse_argv_remainder@Base 0.72.2 ++ rados_conf_parse_env@Base 0.72.2 ++ rados_conf_read_file@Base 0.72.2 ++ rados_conf_set@Base 0.72.2 ++ rados_connect@Base 0.72.2 ++ rados_create2@Base 0.72.2 ++ rados_create@Base 0.72.2 ++ rados_create_read_op@Base 0.79 ++ rados_create_with_context@Base 0.72.2 ++ rados_create_write_op@Base 0.79 ++ rados_exec@Base 0.72.2 ++ rados_get_instance_id@Base 0.72.2 ++ rados_get_last_version@Base 0.72.2 ++ rados_get_min_compatible_client@Base 13.2.0 ++ rados_get_min_compatible_osd@Base 14.2.0 ++ rados_getaddrs@Base 14.2.15 ++ rados_getxattr@Base 0.72.2 ++ rados_getxattrs@Base 0.72.2 ++ rados_getxattrs_end@Base 0.72.2 ++ rados_getxattrs_next@Base 0.72.2 ++ rados_inconsistent_pg_list@Base 10.1.0 ++ rados_ioctx_cct@Base 0.72.2 ++ rados_ioctx_create2@Base 0.93 ++ rados_ioctx_create@Base 0.72.2 ++ rados_ioctx_destroy@Base 0.72.2 ++ rados_ioctx_get_cluster@Base 0.72.2 ++ rados_ioctx_get_id@Base 0.72.2 ++ rados_ioctx_get_namespace@Base 14.2.0 ++ rados_ioctx_get_pool_name@Base 0.72.2 ++ rados_ioctx_locator_set_key@Base 0.72.2 ++ rados_ioctx_pool_get_auid@Base 0.72.2 ++ rados_ioctx_pool_required_alignment2@Base 10.1.0 ++ rados_ioctx_pool_required_alignment@Base 0.79 ++ rados_ioctx_pool_requires_alignment2@Base 10.1.0 ++ rados_ioctx_pool_requires_alignment@Base 0.79 ++ rados_ioctx_pool_set_auid@Base 0.72.2 ++ rados_ioctx_pool_stat@Base 0.72.2 ++ rados_ioctx_pool_stat@LIBRADOS_14.2.0 14.2.0 ++ rados_ioctx_selfmanaged_snap_create@Base 0.72.2 ++ rados_ioctx_selfmanaged_snap_remove@Base 0.72.2 ++ rados_ioctx_selfmanaged_snap_rollback@Base 0.72.2 ++ rados_ioctx_selfmanaged_snap_set_write_ctx@Base 0.72.2 ++ rados_ioctx_set_namespace@Base 0.72.2 ++ rados_ioctx_snap_create@Base 0.72.2 ++ rados_ioctx_snap_get_name@Base 0.72.2 ++ rados_ioctx_snap_get_stamp@Base 0.72.2 ++ rados_ioctx_snap_list@Base 0.72.2 ++ rados_ioctx_snap_lookup@Base 0.72.2 ++ rados_ioctx_snap_remove@Base 0.72.2 ++ rados_ioctx_snap_rollback@Base 0.80~rc1 ++ rados_ioctx_snap_set_read@Base 0.72.2 ++ rados_list_lockers@Base 0.72.2 ++ rados_lock_exclusive@Base 0.72.2 ++ rados_lock_shared@Base 0.72.2 ++ rados_mgr_command@Base 12.0.3 ++ rados_mon_command@Base 0.72.2 ++ rados_mon_command_target@Base 0.72.2 ++ rados_monitor_log2@Base 12.1.4 ++ rados_monitor_log@Base 0.72.2 ++ rados_nobjects_list_close@Base 0.93 ++ rados_nobjects_list_get_cursor@Base 12.0.3 ++ rados_nobjects_list_get_pg_hash_position@Base 0.93 ++ rados_nobjects_list_next@Base 0.93 ++ rados_nobjects_list_open@Base 0.93 ++ rados_nobjects_list_seek@Base 0.93 ++ rados_nobjects_list_seek_cursor@Base 12.0.3 ++ rados_notify2@Base 0.93 ++ rados_notify@Base 0.72.2 ++ rados_notify_ack@Base 0.93 ++ rados_object_list@Base 10.1.0 ++ rados_object_list_begin@Base 10.1.0 ++ rados_object_list_cursor_cmp@Base 10.1.0 ++ rados_object_list_cursor_free@Base 10.1.0 ++ rados_object_list_end@Base 10.1.0 ++ rados_object_list_free@Base 10.1.0 ++ rados_object_list_is_end@Base 10.1.0 ++ rados_object_list_slice@Base 10.1.0 ++ rados_objects_list_close@Base 0.72.2 ++ rados_objects_list_get_pg_hash_position@Base 0.79 ++ rados_objects_list_next@Base 0.72.2 ++ rados_objects_list_open@Base 0.72.2 ++ rados_objects_list_seek@Base 0.79 ++ rados_omap_get_end@Base 0.79 ++ rados_omap_get_next2@Base 13.2.0 ++ rados_omap_get_next@Base 0.79 ++ rados_omap_iter_size@Base 14.2.0 ++ rados_osd_command@Base 0.72.2 ++ rados_pg_command@Base 0.72.2 ++ rados_ping_monitor@Base 0.72.2 ++ rados_pool_create@Base 0.72.2 ++ rados_pool_create_with_all@Base 0.72.2 ++ rados_pool_create_with_auid@Base 0.72.2 ++ rados_pool_create_with_crush_rule@Base 0.72.2 ++ rados_pool_delete@Base 0.72.2 ++ rados_pool_get_base_tier@Base 0.93 ++ rados_pool_list@Base 0.72.2 ++ rados_pool_lookup@Base 0.72.2 ++ rados_pool_reverse_lookup@Base 0.72.2 ++ rados_read@Base 0.72.2 ++ rados_read_op_assert_exists@Base 0.79 ++ rados_read_op_assert_version@Base 0.93 ++ rados_read_op_checksum@Base 12.0.3 ++ rados_read_op_cmpext@Base 12.0.3 ++ rados_read_op_cmpxattr@Base 0.79 ++ rados_read_op_exec@Base 0.79 ++ rados_read_op_exec_user_buf@Base 0.79 ++ rados_read_op_getxattrs@Base 0.79 ++ rados_read_op_omap_cmp2@Base 13.2.0 ++ rados_read_op_omap_cmp@Base 0.79 ++ rados_read_op_omap_get_keys2@Base 12.0.3 ++ rados_read_op_omap_get_keys@Base 0.79 ++ rados_read_op_omap_get_vals2@Base 12.0.3 ++ rados_read_op_omap_get_vals@Base 0.79 ++ rados_read_op_omap_get_vals_by_keys2@Base 13.2.0 ++ rados_read_op_omap_get_vals_by_keys@Base 0.79 ++ rados_read_op_operate@Base 0.79 ++ rados_read_op_read@Base 0.79 ++ rados_read_op_set_flags@Base 0.79 ++ rados_read_op_stat@Base 0.79 ++ rados_release_read_op@Base 0.79 ++ rados_release_write_op@Base 0.79 ++ rados_remove@Base 0.72.2 ++ rados_rmxattr@Base 0.72.2 ++ rados_rollback@Base 0.72.2 ++ rados_service_register@Base 12.1.4 ++ rados_service_update_status@Base 12.1.4 ++ rados_set_alloc_hint2@Base 12.0.3 ++ rados_set_alloc_hint@Base 0.79 ++ rados_set_osdmap_full_try@Base 13.2.0 ++ rados_setxattr@Base 0.72.2 ++ rados_shutdown@Base 0.72.2 ++ rados_stat@Base 0.72.2 ++ rados_tmap_get@Base 0.72.2 ++ rados_tmap_get@LIBRADOS_14.2.0 14.2.0 ++ rados_tmap_put@Base 0.72.2 ++ rados_tmap_put@LIBRADOS_14.2.0 14.2.0 ++ rados_tmap_update@Base 0.72.2 ++ rados_tmap_update@LIBRADOS_14.2.0 14.2.0 ++ rados_trunc@Base 0.72.2 ++ rados_unlock@Base 0.72.2 ++ rados_unset_osdmap_full_try@Base 13.2.0 ++ rados_unwatch2@Base 0.93 ++ rados_unwatch@Base 0.72.2 ++ rados_version@Base 0.72.2 ++ rados_wait_for_latest_osdmap@Base 0.79 ++ rados_watch2@Base 0.93 ++ rados_watch3@Base 12.0.3 ++ rados_watch@Base 0.72.2 ++ rados_watch_check@Base 0.93 ++ rados_watch_flush@Base 0.93 ++ rados_write@Base 0.72.2 ++ rados_write_full@Base 0.72.2 ++ rados_write_op_append@Base 0.79 ++ rados_write_op_assert_exists@Base 0.79 ++ rados_write_op_assert_version@Base 0.93 ++ rados_write_op_cmpext@Base 12.0.3 ++ rados_write_op_cmpxattr@Base 0.79 ++ rados_write_op_create@Base 0.79 ++ rados_write_op_exec@Base 0.79 ++ rados_write_op_omap_clear@Base 0.79 ++ rados_write_op_omap_cmp2@Base 13.2.0 ++ rados_write_op_omap_cmp@Base 0.79 ++ rados_write_op_omap_rm_keys2@Base 13.2.0 ++ rados_write_op_omap_rm_keys@Base 0.79 ++ rados_write_op_omap_set2@Base 13.2.0 ++ rados_write_op_omap_set@Base 0.79 ++ rados_write_op_operate2@Base 10.1.0 ++ rados_write_op_operate@Base 0.79 ++ rados_write_op_remove@Base 0.79 ++ rados_write_op_rmxattr@Base 0.79 ++ rados_write_op_set_alloc_hint2@Base 12.0.3 ++ rados_write_op_set_alloc_hint@Base 0.79 ++ rados_write_op_set_flags@Base 0.79 ++ rados_write_op_setxattr@Base 0.79 ++ rados_write_op_truncate@Base 0.79 ++ rados_write_op_write@Base 0.79 ++ rados_write_op_write_full@Base 0.79 ++ rados_write_op_writesame@Base 12.0.3 ++ rados_write_op_zero@Base 0.79 ++ rados_writesame@Base 12.0.3 diff --cc debian/libradosstriper-dev.install index f7986d30f,000000000..4d6d7b5a0 mode 100644,000000..100644 --- a/debian/libradosstriper-dev.install +++ b/debian/libradosstriper-dev.install @@@ -1,3 -1,0 +1,3 @@@ +usr/include/radosstriper/libradosstriper.h +usr/include/radosstriper/libradosstriper.hpp - usr/lib/libradosstriper.so ++usr/lib/*/libradosstriper.so diff --cc debian/libradosstriper1.install index 46235acff,000000000..742549ba9 mode 100644,000000..100644 --- a/debian/libradosstriper1.install +++ b/debian/libradosstriper1.install @@@ -1,1 -1,0 +1,1 @@@ - usr/lib/libradosstriper.so.* ++usr/lib/*/libradosstriper.so.* diff --cc debian/libradosstriper1.symbols index 000000000,000000000..7fc1e362e new file mode 100644 --- /dev/null +++ b/debian/libradosstriper1.symbols @@@ -1,0 -1,0 +1,39 @@@ ++libradosstriper.so.1 libradosstriper1 #MINVER# ++ (regex|c++)"^_.*" 0.87 ++ default_file_layout@Base 10.1.0 ++ rados_striper_aio_append@Base 0.87 ++ rados_striper_aio_flush@Base 0.87 ++ rados_striper_aio_read@Base 0.87 ++ rados_striper_aio_remove@Base 12.0.3 ++ rados_striper_aio_stat@Base 12.0.3 ++ rados_striper_aio_write@Base 0.87 ++ rados_striper_aio_write_full@Base 0.87 ++ rados_striper_append@Base 0.87 ++ rados_striper_create@Base 0.87 ++ rados_striper_destroy@Base 0.87 ++ rados_striper_getxattr@Base 0.87 ++ rados_striper_getxattrs@Base 0.87 ++ rados_striper_getxattrs_end@Base 0.87 ++ rados_striper_getxattrs_next@Base 0.87 ++ rados_striper_multi_aio_create_completion@Base 0.87 ++ rados_striper_multi_aio_get_return_value@Base 0.87 ++ rados_striper_multi_aio_is_complete@Base 0.87 ++ rados_striper_multi_aio_is_complete_and_cb@Base 0.87 ++ rados_striper_multi_aio_is_safe@Base 0.87 ++ rados_striper_multi_aio_is_safe_and_cb@Base 0.87 ++ rados_striper_multi_aio_release@Base 0.87 ++ rados_striper_multi_aio_wait_for_complete@Base 0.87 ++ rados_striper_multi_aio_wait_for_complete_and_cb@Base 0.87 ++ rados_striper_multi_aio_wait_for_safe@Base 0.87 ++ rados_striper_multi_aio_wait_for_safe_and_cb@Base 0.87 ++ rados_striper_read@Base 0.87 ++ rados_striper_remove@Base 0.87 ++ rados_striper_rmxattr@Base 0.87 ++ rados_striper_set_object_layout_object_size@Base 0.87 ++ rados_striper_set_object_layout_stripe_count@Base 0.87 ++ rados_striper_set_object_layout_stripe_unit@Base 0.87 ++ rados_striper_setxattr@Base 0.87 ++ rados_striper_stat@Base 0.87 ++ rados_striper_trunc@Base 0.87 ++ rados_striper_write@Base 0.87 ++ rados_striper_write_full@Base 0.87 diff --cc debian/librbd-dev.install index 6028f4288,000000000..c6b455e58 mode 100644,000000..100644 --- a/debian/librbd-dev.install +++ b/debian/librbd-dev.install @@@ -1,5 -1,0 +1,4 @@@ +usr/include/rbd/features.h +usr/include/rbd/librbd.h +usr/include/rbd/librbd.hpp - usr/lib/librbd.so - usr/lib/librbd_tp.so ++usr/lib/*/librbd.so diff --cc debian/librbd1.install index 047953801,000000000..decadbc19 mode 100644,000000..100644 --- a/debian/librbd1.install +++ b/debian/librbd1.install @@@ -1,2 -1,0 +1,1 @@@ - usr/lib/librbd.so.* - usr/lib/librbd_tp.so.* ++usr/lib/*/librbd.so.* diff --cc debian/librbd1.symbols index 000000000,000000000..2e2f873d5 new file mode 100644 --- /dev/null +++ b/debian/librbd1.symbols @@@ -1,0 -1,0 +1,242 @@@ ++librbd.so.1 librbd1 #MINVER# ++ (regex|c++)"^_.*" 0.87 ++ rbd_aio_close@Base 10.1.0 ++ rbd_aio_compare_and_write@Base 12.1.4 ++ rbd_aio_create_completion@Base 0.72.2 ++ rbd_aio_discard@Base 0.72.2 ++ rbd_aio_flush@Base 0.72.2 ++ rbd_aio_get_arg@Base 10.1.0 ++ rbd_aio_get_return_value@Base 0.72.2 ++ rbd_aio_is_complete@Base 0.72.2 ++ rbd_aio_mirror_image_demote@Base 12.0.3 ++ rbd_aio_mirror_image_get_info@Base 12.0.3 ++ rbd_aio_mirror_image_get_status@Base 12.0.3 ++ rbd_aio_mirror_image_promote@Base 12.0.3 ++ rbd_aio_open@Base 10.1.0 ++ rbd_aio_open_by_id@Base 12.0.3 ++ rbd_aio_open_by_id_read_only@Base 12.0.3 ++ rbd_aio_open_read_only@Base 10.1.0 ++ rbd_aio_read2@Base 0.93 ++ rbd_aio_read@Base 0.72.2 ++ rbd_aio_readv@Base 12.0.3 ++ rbd_aio_release@Base 0.72.2 ++ rbd_aio_wait_for_complete@Base 0.72.2 ++ rbd_aio_write2@Base 0.93 ++ rbd_aio_write@Base 0.72.2 ++ rbd_aio_write_zeroes@Base 14.2.15 ++ rbd_aio_writesame@Base 12.0.3 ++ rbd_aio_writev@Base 12.0.3 ++ rbd_break_lock@Base 0.72.2 ++ rbd_clone2@Base 0.72.2 ++ rbd_clone3@Base 10.1.0 ++ rbd_clone@Base 0.72.2 ++ rbd_close@Base 0.72.2 ++ rbd_compare_and_write@Base 12.1.4 ++ rbd_config_image_list@Base 14.2.0 ++ rbd_config_image_list_cleanup@Base 14.2.0 ++ rbd_config_pool_list@Base 14.2.0 ++ rbd_config_pool_list_cleanup@Base 14.2.0 ++ rbd_copy2@Base 0.72.2 ++ rbd_copy3@Base 10.1.0 ++ rbd_copy4@Base 12.0.3 ++ rbd_copy@Base 0.72.2 ++ rbd_copy_with_progress2@Base 0.72.2 ++ rbd_copy_with_progress3@Base 12.0.3 ++ rbd_copy_with_progress4@Base 12.0.3 ++ rbd_copy_with_progress@Base 0.72.2 ++ rbd_create2@Base 0.72.2 ++ rbd_create3@Base 0.72.2 ++ rbd_create4@Base 10.1.0 ++ rbd_create@Base 0.72.2 ++ rbd_deep_copy@Base 13.2.0 ++ rbd_deep_copy_with_progress@Base 13.2.0 ++ rbd_diff_iterate2@Base 9.2.0 ++ rbd_diff_iterate@Base 0.72.2 ++ rbd_discard@Base 0.72.2 ++ rbd_flatten@Base 0.72.2 ++ rbd_flatten_with_progress@Base 12.0.3 ++ rbd_flush@Base 0.72.2 ++ rbd_get_access_timestamp@Base 14.2.0 ++ rbd_get_block_name_prefix@Base 12.0.3 ++ rbd_get_create_timestamp@Base 12.1.4 ++ rbd_get_data_pool_id@Base 12.0.3 ++ rbd_get_features@Base 0.72.2 ++ rbd_get_flags@Base 0.93 ++ rbd_get_group@Base 13.2.0 ++ rbd_get_id@Base 12.0.3 ++ rbd_get_modify_timestamp@Base 14.2.0 ++ rbd_get_name@Base 13.2.0 ++ rbd_get_old_format@Base 0.72.2 ++ rbd_get_op_features@Base 13.2.0 ++ rbd_get_overlap@Base 0.72.2 ++ rbd_get_parent@Base 14.2.0 ++ rbd_get_parent_info2@Base 12.0.3 ++ rbd_get_parent_info@Base 0.72.2 ++ rbd_get_size@Base 0.72.2 ++ rbd_get_stripe_count@Base 0.72.2 ++ rbd_get_stripe_unit@Base 0.72.2 ++ rbd_group_create@Base 13.2.0 ++ rbd_group_image_add@Base 13.2.0 ++ rbd_group_image_list@Base 13.2.0 ++ rbd_group_image_list_cleanup@Base 13.2.0 ++ rbd_group_image_remove@Base 13.2.0 ++ rbd_group_image_remove_by_id@Base 13.2.0 ++ rbd_group_info_cleanup@Base 13.2.0 ++ rbd_group_list@Base 13.2.0 ++ rbd_group_remove@Base 13.2.0 ++ rbd_group_rename@Base 13.2.0 ++ rbd_group_snap_create@Base 13.2.0 ++ rbd_group_snap_list@Base 13.2.0 ++ rbd_group_snap_list_cleanup@Base 13.2.0 ++ rbd_group_snap_remove@Base 13.2.0 ++ rbd_group_snap_rename@Base 13.2.0 ++ rbd_group_snap_rollback@Base 14.2.0 ++ rbd_group_snap_rollback_with_progress@Base 14.2.0 ++ rbd_image_options_clear@Base 10.1.0 ++ rbd_image_options_create@Base 10.1.0 ++ rbd_image_options_destroy@Base 10.1.0 ++ rbd_image_options_get_string@Base 10.1.0 ++ rbd_image_options_get_uint64@Base 10.1.0 ++ rbd_image_options_is_empty@Base 10.1.0 ++ rbd_image_options_is_set@Base 12.0.3 ++ rbd_image_options_set_string@Base 10.1.0 ++ rbd_image_options_set_uint64@Base 10.1.0 ++ rbd_image_options_unset@Base 10.1.0 ++ rbd_image_spec_cleanup@Base 14.2.0 ++ rbd_image_spec_list_cleanup@Base 14.2.0 ++ rbd_invalidate_cache@Base 0.80.5-2~ ++ rbd_is_exclusive_lock_owner@Base 0.93 ++ rbd_linked_image_spec_cleanup@Base 14.2.0 ++ rbd_linked_image_spec_list_cleanup@Base 14.2.0 ++ rbd_list2@Base 14.2.0 ++ rbd_list@Base 0.72.2 ++ rbd_list_child_cleanup@Base 13.2.0 ++ rbd_list_children2@Base 13.2.0 ++ rbd_list_children3@Base 14.2.0 ++ rbd_list_children@Base 0.72.2 ++ rbd_list_children_cleanup@Base 13.2.0 ++ rbd_list_descendants@Base 14.2.0 ++ rbd_list_lockers@Base 0.72.2 ++ rbd_lock_acquire@Base 12.0.3 ++ rbd_lock_break@Base 12.0.3 ++ rbd_lock_exclusive@Base 0.72.2 ++ rbd_lock_get_owners@Base 12.0.3 ++ rbd_lock_get_owners_cleanup@Base 12.0.3 ++ rbd_lock_release@Base 12.0.3 ++ rbd_lock_shared@Base 0.72.2 ++ rbd_metadata_get@Base 9.2.0 ++ rbd_metadata_list@Base 9.2.0 ++ rbd_metadata_remove@Base 9.2.0 ++ rbd_metadata_set@Base 9.2.0 ++ rbd_migration_abort@Base 14.2.0 ++ rbd_migration_abort_with_progress@Base 14.2.0 ++ rbd_migration_commit@Base 14.2.0 ++ rbd_migration_commit_with_progress@Base 14.2.0 ++ rbd_migration_execute@Base 14.2.0 ++ rbd_migration_execute_with_progress@Base 14.2.0 ++ rbd_migration_prepare@Base 14.2.0 ++ rbd_migration_status@Base 14.2.0 ++ rbd_migration_status_cleanup@Base 14.2.0 ++ rbd_mirror_image_demote@Base 10.1.0 ++ rbd_mirror_image_disable@Base 10.1.0 ++ rbd_mirror_image_enable@Base 10.1.0 ++ rbd_mirror_image_get_info@Base 10.1.0 ++ rbd_mirror_image_get_instance_id@Base 14.2.0 ++ rbd_mirror_image_get_status@Base 12.0.3 ++ rbd_mirror_image_instance_id_list@Base 14.2.0 ++ rbd_mirror_image_instance_id_list_cleanup@Base 14.2.0 ++ rbd_mirror_image_promote@Base 10.1.0 ++ rbd_mirror_image_resync@Base 10.1.0 ++ rbd_mirror_image_status_list@Base 12.0.3 ++ rbd_mirror_image_status_list_cleanup@Base 12.0.3 ++ rbd_mirror_image_status_summary@Base 12.0.3 ++ rbd_mirror_mode_get@Base 10.1.0 ++ rbd_mirror_mode_set@Base 10.1.0 ++ rbd_mirror_peer_add@Base 10.1.0 ++ rbd_mirror_peer_bootstrap_create@Base 14.2.5 ++ rbd_mirror_peer_bootstrap_import@Base 14.2.5 ++ rbd_mirror_peer_get_attributes@Base 14.2.0 ++ rbd_mirror_peer_list@Base 10.1.0 ++ rbd_mirror_peer_list_cleanup@Base 10.1.0 ++ rbd_mirror_peer_remove@Base 10.1.0 ++ rbd_mirror_peer_set_attributes@Base 14.2.0 ++ rbd_mirror_peer_set_client@Base 10.1.0 ++ rbd_mirror_peer_set_cluster@Base 10.1.0 ++ rbd_mirror_site_name_get@Base 14.2.5 ++ rbd_mirror_site_name_set@Base 14.2.5 ++ rbd_namespace_create@Base 14.2.0 ++ rbd_namespace_exists@Base 14.2.0 ++ rbd_namespace_list@Base 14.2.0 ++ rbd_namespace_remove@Base 14.2.0 ++ rbd_open@Base 0.72.2 ++ rbd_open_by_id@Base 12.0.3 ++ rbd_open_by_id_read_only@Base 12.0.3 ++ rbd_open_read_only@Base 0.72.2 ++ rbd_poll_io_events@Base 10.1.0 ++ rbd_pool_init@Base 14.2.0 ++ rbd_pool_metadata_get@Base 14.2.0 ++ rbd_pool_metadata_list@Base 14.2.0 ++ rbd_pool_metadata_remove@Base 14.2.0 ++ rbd_pool_metadata_set@Base 14.2.0 ++ rbd_pool_stats_create@Base 14.2.0 ++ rbd_pool_stats_destroy@Base 14.2.0 ++ rbd_pool_stats_get@Base 14.2.0 ++ rbd_pool_stats_option_add_uint64@Base 14.2.0 ++ rbd_read2@Base 0.93 ++ rbd_read@Base 0.72.2 ++ rbd_read_iterate2@Base 0.72.2 ++ rbd_read_iterate@Base 0.72.2 ++ rbd_rebuild_object_map@Base 9.2.0 ++ rbd_remove@Base 0.72.2 ++ rbd_remove_with_progress@Base 0.72.2 ++ rbd_rename@Base 0.72.2 ++ rbd_resize2@Base 12.0.3 ++ rbd_resize@Base 0.72.2 ++ rbd_resize_with_progress@Base 0.72.2 ++ rbd_set_image_notification@Base 10.1.0 ++ rbd_snap_create@Base 0.72.2 ++ rbd_snap_get_group_namespace@Base 13.2.0 ++ rbd_snap_get_limit@Base 12.0.3 ++ rbd_snap_get_namespace_type@Base 13.2.0 ++ rbd_snap_get_timestamp@Base 12.0.3 ++ rbd_snap_get_trash_namespace@Base 14.2.0 ++ rbd_snap_group_namespace_cleanup@Base 13.2.0 ++ rbd_snap_is_protected@Base 0.72.2 ++ rbd_snap_list@Base 0.72.2 ++ rbd_snap_list_end@Base 0.72.2 ++ rbd_snap_protect@Base 0.72.2 ++ rbd_snap_remove2@Base 12.0.3 ++ rbd_snap_remove@Base 0.72.2 ++ rbd_snap_remove_by_id@Base 14.2.0 ++ rbd_snap_rename@Base 10.1.0 ++ rbd_snap_rollback@Base 0.72.2 ++ rbd_snap_rollback_with_progress@Base 0.72.2 ++ rbd_snap_set@Base 0.72.2 ++ rbd_snap_set_by_id@Base 13.2.0 ++ rbd_snap_set_limit@Base 12.0.3 ++ rbd_snap_spec_cleanup@Base 14.2.0 ++ rbd_snap_unprotect@Base 0.72.2 ++ rbd_sparsify@Base 14.2.0 ++ rbd_sparsify_with_progress@Base 14.2.0 ++ rbd_stat@Base 0.72.2 ++ rbd_trash_get@Base 12.0.3 ++ rbd_trash_get_cleanup@Base 12.0.3 ++ rbd_trash_list@Base 12.0.3 ++ rbd_trash_list_cleanup@Base 12.0.3 ++ rbd_trash_move@Base 12.0.3 ++ rbd_trash_purge@Base 14.2.0 ++ rbd_trash_purge_with_progress@Base 14.2.0 ++ rbd_trash_remove@Base 12.0.3 ++ rbd_trash_remove_with_progress@Base 12.0.3 ++ rbd_trash_restore@Base 12.0.3 ++ rbd_unlock@Base 0.72.2 ++ rbd_update_features@Base 9.2.0 ++ rbd_update_unwatch@Base 12.0.3 ++ rbd_update_watch@Base 12.0.3 ++ rbd_version@Base 0.72.2 ++ rbd_watchers_list@Base 13.2.0 ++ rbd_watchers_list_cleanup@Base 13.2.0 ++ rbd_write2@Base 0.93 ++ rbd_write@Base 0.72.2 ++ rbd_write_zeroes@Base 14.2.15 ++ rbd_writesame@Base 12.0.3 diff --cc debian/librgw-dev.install index 1f4f1b190,000000000..1fff6e4f0 mode 100644,000000..100644 --- a/debian/librgw-dev.install +++ b/debian/librgw-dev.install @@@ -1,5 -1,0 +1,5 @@@ +usr/include/rados/librgw.h +usr/include/rados/librgw_admin_user.h +usr/include/rados/rgw_file.h - usr/lib/librgw.so - usr/lib/librgw_admin_user.so ++usr/lib/*/librgw.so ++usr/lib/*/librgw_admin_user.so diff --cc debian/librgw2.install index 725f341af,000000000..c6fd3ab59 mode 100644,000000..100644 --- a/debian/librgw2.install +++ b/debian/librgw2.install @@@ -1,2 -1,0 +1,2 @@@ - usr/lib/librgw.so.* - usr/lib/librgw_admin_user.so.* ++usr/lib/*/librgw.so.* ++usr/lib/*/librgw_admin_user.so.* diff --cc debian/man/ceph-crush-location.1 index 000000000,000000000..d05d85c7b new file mode 100644 --- /dev/null +++ b/debian/man/ceph-crush-location.1 @@@ -1,0 -1,0 +1,24 @@@ ++.TH ceph-crush-location "1" "April 2014" "ceph-crush-location" "User Commands" ++.SH NAME ++ceph-crush-location \- get CRUSH location ++.SH DESCRIPTION ++Generate a CRUSH location for the given entity ++ ++The CRUSH location consists of a list of key=value pairs, separated ++by spaces, all on a single line. This describes where in CRUSH ++hierarhcy this entity should be placed. ++ ++.SH OPTIONS ++.TP 4 ++\fB\-\-cluster\fR ++name of the cluster (see /etc/ceph/$cluster.conf) ++.TP 4 ++\fB\-\-type\fR ++daemon/entity type ++.TP 4 ++\fB\-\-id\fR ++id (osd number, mds name, client name) ++ ++.SH SEE ALSO ++.TP ++\fBceph-conf\fP(8) diff --cc debian/man/mount.fuse.ceph.8 index 000000000,000000000..718936578 new file mode 100644 --- /dev/null +++ b/debian/man/mount.fuse.ceph.8 @@@ -1,0 -1,0 +1,30 @@@ ++.TH mount.fuse.ceph "8" "March 2014" "ceph-fuse" "User Commands" ++.SH NAME ++mount.fuse.ceph \- wrapper around ceph-fuse ++.SH DESCRIPTION ++Helper to mount ceph-fuse from /etc/fstab. To use, add an entry like: ++ ++.nf ++# DEVICE PATH TYPE OPTIONS ++ mount.fuse.ceph#conf=/etc/ceph/ceph.conf,id=admin /mnt/ceph fuse _netdev,noatime,allow_other 0 0 ++ mount.fuse.ceph#conf=/etc/ceph/foo.conf,id=myuser /mnt/ceph2 fuse _netdev,noatime,allow_other 0 0 ++.fi ++ ++where the device field is a comma-separated list of options to pass on ++the command line. The examples above, for example, specify that ++ceph-fuse will authenticated as client.admin and client.myuser ++(respectively), and the second example also sets the "conf" option to ++"/etc/ceph/foo.conf" via the ceph-fuse command line. Any valid ++ceph-fuse option can be passed in this way. ++ ++.SH OPTIONS ++.TP 4 ++\fB\-\-conf\fR ++path to ceph cponfiguration file, usually "/etc/ceph/ceph.conf" ++.TP 4 ++\fB\-\-id\fR ++user name ++ ++.SH SEE ALSO ++.TP ++\fBceph-fuse\fP(8) diff --cc debian/missing-sources/bootstrap.js index 000000000,000000000..8a2e99a53 new file mode 100644 --- /dev/null +++ b/debian/missing-sources/bootstrap.js @@@ -1,0 -1,0 +1,2377 @@@ ++/*! ++ * Bootstrap v3.3.7 (http://getbootstrap.com) ++ * Copyright 2011-2016 Twitter, Inc. ++ * Licensed under the MIT license ++ */ ++ ++if (typeof jQuery === 'undefined') { ++ throw new Error('Bootstrap\'s JavaScript requires jQuery') ++} ++ +++function ($) { ++ 'use strict'; ++ var version = $.fn.jquery.split(' ')[0].split('.') ++ if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 3)) { ++ throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4') ++ } ++}(jQuery); ++ ++/* ======================================================================== ++ * Bootstrap: transition.js v3.3.7 ++ * http://getbootstrap.com/javascript/#transitions ++ * ======================================================================== ++ * Copyright 2011-2016 Twitter, Inc. ++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) ++ * ======================================================================== */ ++ ++ +++function ($) { ++ 'use strict'; ++ ++ // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) ++ // ============================================================ ++ ++ function transitionEnd() { ++ var el = document.createElement('bootstrap') ++ ++ var transEndEventNames = { ++ WebkitTransition : 'webkitTransitionEnd', ++ MozTransition : 'transitionend', ++ OTransition : 'oTransitionEnd otransitionend', ++ transition : 'transitionend' ++ } ++ ++ for (var name in transEndEventNames) { ++ if (el.style[name] !== undefined) { ++ return { end: transEndEventNames[name] } ++ } ++ } ++ ++ return false // explicit for ie8 ( ._.) ++ } ++ ++ // http://blog.alexmaccaw.com/css-transitions ++ $.fn.emulateTransitionEnd = function (duration) { ++ var called = false ++ var $el = this ++ $(this).one('bsTransitionEnd', function () { called = true }) ++ var callback = function () { if (!called) $($el).trigger($.support.transition.end) } ++ setTimeout(callback, duration) ++ return this ++ } ++ ++ $(function () { ++ $.support.transition = transitionEnd() ++ ++ if (!$.support.transition) return ++ ++ $.event.special.bsTransitionEnd = { ++ bindType: $.support.transition.end, ++ delegateType: $.support.transition.end, ++ handle: function (e) { ++ if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) ++ } ++ } ++ }) ++ ++}(jQuery); ++ ++/* ======================================================================== ++ * Bootstrap: alert.js v3.3.7 ++ * http://getbootstrap.com/javascript/#alerts ++ * ======================================================================== ++ * Copyright 2011-2016 Twitter, Inc. ++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) ++ * ======================================================================== */ ++ ++ +++function ($) { ++ 'use strict'; ++ ++ // ALERT CLASS DEFINITION ++ // ====================== ++ ++ var dismiss = '[data-dismiss="alert"]' ++ var Alert = function (el) { ++ $(el).on('click', dismiss, this.close) ++ } ++ ++ Alert.VERSION = '3.3.7' ++ ++ Alert.TRANSITION_DURATION = 150 ++ ++ Alert.prototype.close = function (e) { ++ var $this = $(this) ++ var selector = $this.attr('data-target') ++ ++ if (!selector) { ++ selector = $this.attr('href') ++ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 ++ } ++ ++ var $parent = $(selector === '#' ? [] : selector) ++ ++ if (e) e.preventDefault() ++ ++ if (!$parent.length) { ++ $parent = $this.closest('.alert') ++ } ++ ++ $parent.trigger(e = $.Event('close.bs.alert')) ++ ++ if (e.isDefaultPrevented()) return ++ ++ $parent.removeClass('in') ++ ++ function removeElement() { ++ // detach from parent, fire event then clean up data ++ $parent.detach().trigger('closed.bs.alert').remove() ++ } ++ ++ $.support.transition && $parent.hasClass('fade') ? ++ $parent ++ .one('bsTransitionEnd', removeElement) ++ .emulateTransitionEnd(Alert.TRANSITION_DURATION) : ++ removeElement() ++ } ++ ++ ++ // ALERT PLUGIN DEFINITION ++ // ======================= ++ ++ function Plugin(option) { ++ return this.each(function () { ++ var $this = $(this) ++ var data = $this.data('bs.alert') ++ ++ if (!data) $this.data('bs.alert', (data = new Alert(this))) ++ if (typeof option == 'string') data[option].call($this) ++ }) ++ } ++ ++ var old = $.fn.alert ++ ++ $.fn.alert = Plugin ++ $.fn.alert.Constructor = Alert ++ ++ ++ // ALERT NO CONFLICT ++ // ================= ++ ++ $.fn.alert.noConflict = function () { ++ $.fn.alert = old ++ return this ++ } ++ ++ ++ // ALERT DATA-API ++ // ============== ++ ++ $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) ++ ++}(jQuery); ++ ++/* ======================================================================== ++ * Bootstrap: button.js v3.3.7 ++ * http://getbootstrap.com/javascript/#buttons ++ * ======================================================================== ++ * Copyright 2011-2016 Twitter, Inc. ++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) ++ * ======================================================================== */ ++ ++ +++function ($) { ++ 'use strict'; ++ ++ // BUTTON PUBLIC CLASS DEFINITION ++ // ============================== ++ ++ var Button = function (element, options) { ++ this.$element = $(element) ++ this.options = $.extend({}, Button.DEFAULTS, options) ++ this.isLoading = false ++ } ++ ++ Button.VERSION = '3.3.7' ++ ++ Button.DEFAULTS = { ++ loadingText: 'loading...' ++ } ++ ++ Button.prototype.setState = function (state) { ++ var d = 'disabled' ++ var $el = this.$element ++ var val = $el.is('input') ? 'val' : 'html' ++ var data = $el.data() ++ ++ state += 'Text' ++ ++ if (data.resetText == null) $el.data('resetText', $el[val]()) ++ ++ // push to event loop to allow forms to submit ++ setTimeout($.proxy(function () { ++ $el[val](data[state] == null ? this.options[state] : data[state]) ++ ++ if (state == 'loadingText') { ++ this.isLoading = true ++ $el.addClass(d).attr(d, d).prop(d, true) ++ } else if (this.isLoading) { ++ this.isLoading = false ++ $el.removeClass(d).removeAttr(d).prop(d, false) ++ } ++ }, this), 0) ++ } ++ ++ Button.prototype.toggle = function () { ++ var changed = true ++ var $parent = this.$element.closest('[data-toggle="buttons"]') ++ ++ if ($parent.length) { ++ var $input = this.$element.find('input') ++ if ($input.prop('type') == 'radio') { ++ if ($input.prop('checked')) changed = false ++ $parent.find('.active').removeClass('active') ++ this.$element.addClass('active') ++ } else if ($input.prop('type') == 'checkbox') { ++ if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false ++ this.$element.toggleClass('active') ++ } ++ $input.prop('checked', this.$element.hasClass('active')) ++ if (changed) $input.trigger('change') ++ } else { ++ this.$element.attr('aria-pressed', !this.$element.hasClass('active')) ++ this.$element.toggleClass('active') ++ } ++ } ++ ++ ++ // BUTTON PLUGIN DEFINITION ++ // ======================== ++ ++ function Plugin(option) { ++ return this.each(function () { ++ var $this = $(this) ++ var data = $this.data('bs.button') ++ var options = typeof option == 'object' && option ++ ++ if (!data) $this.data('bs.button', (data = new Button(this, options))) ++ ++ if (option == 'toggle') data.toggle() ++ else if (option) data.setState(option) ++ }) ++ } ++ ++ var old = $.fn.button ++ ++ $.fn.button = Plugin ++ $.fn.button.Constructor = Button ++ ++ ++ // BUTTON NO CONFLICT ++ // ================== ++ ++ $.fn.button.noConflict = function () { ++ $.fn.button = old ++ return this ++ } ++ ++ ++ // BUTTON DATA-API ++ // =============== ++ ++ $(document) ++ .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { ++ var $btn = $(e.target).closest('.btn') ++ Plugin.call($btn, 'toggle') ++ if (!($(e.target).is('input[type="radio"], input[type="checkbox"]'))) { ++ // Prevent double click on radios, and the double selections (so cancellation) on checkboxes ++ e.preventDefault() ++ // The target component still receive the focus ++ if ($btn.is('input,button')) $btn.trigger('focus') ++ else $btn.find('input:visible,button:visible').first().trigger('focus') ++ } ++ }) ++ .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { ++ $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type)) ++ }) ++ ++}(jQuery); ++ ++/* ======================================================================== ++ * Bootstrap: carousel.js v3.3.7 ++ * http://getbootstrap.com/javascript/#carousel ++ * ======================================================================== ++ * Copyright 2011-2016 Twitter, Inc. ++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) ++ * ======================================================================== */ ++ ++ +++function ($) { ++ 'use strict'; ++ ++ // CAROUSEL CLASS DEFINITION ++ // ========================= ++ ++ var Carousel = function (element, options) { ++ this.$element = $(element) ++ this.$indicators = this.$element.find('.carousel-indicators') ++ this.options = options ++ this.paused = null ++ this.sliding = null ++ this.interval = null ++ this.$active = null ++ this.$items = null ++ ++ this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) ++ ++ this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element ++ .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) ++ .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) ++ } ++ ++ Carousel.VERSION = '3.3.7' ++ ++ Carousel.TRANSITION_DURATION = 600 ++ ++ Carousel.DEFAULTS = { ++ interval: 5000, ++ pause: 'hover', ++ wrap: true, ++ keyboard: true ++ } ++ ++ Carousel.prototype.keydown = function (e) { ++ if (/input|textarea/i.test(e.target.tagName)) return ++ switch (e.which) { ++ case 37: this.prev(); break ++ case 39: this.next(); break ++ default: return ++ } ++ ++ e.preventDefault() ++ } ++ ++ Carousel.prototype.cycle = function (e) { ++ e || (this.paused = false) ++ ++ this.interval && clearInterval(this.interval) ++ ++ this.options.interval ++ && !this.paused ++ && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) ++ ++ return this ++ } ++ ++ Carousel.prototype.getItemIndex = function (item) { ++ this.$items = item.parent().children('.item') ++ return this.$items.index(item || this.$active) ++ } ++ ++ Carousel.prototype.getItemForDirection = function (direction, active) { ++ var activeIndex = this.getItemIndex(active) ++ var willWrap = (direction == 'prev' && activeIndex === 0) ++ || (direction == 'next' && activeIndex == (this.$items.length - 1)) ++ if (willWrap && !this.options.wrap) return active ++ var delta = direction == 'prev' ? -1 : 1 ++ var itemIndex = (activeIndex + delta) % this.$items.length ++ return this.$items.eq(itemIndex) ++ } ++ ++ Carousel.prototype.to = function (pos) { ++ var that = this ++ var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) ++ ++ if (pos > (this.$items.length - 1) || pos < 0) return ++ ++ if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" ++ if (activeIndex == pos) return this.pause().cycle() ++ ++ return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) ++ } ++ ++ Carousel.prototype.pause = function (e) { ++ e || (this.paused = true) ++ ++ if (this.$element.find('.next, .prev').length && $.support.transition) { ++ this.$element.trigger($.support.transition.end) ++ this.cycle(true) ++ } ++ ++ this.interval = clearInterval(this.interval) ++ ++ return this ++ } ++ ++ Carousel.prototype.next = function () { ++ if (this.sliding) return ++ return this.slide('next') ++ } ++ ++ Carousel.prototype.prev = function () { ++ if (this.sliding) return ++ return this.slide('prev') ++ } ++ ++ Carousel.prototype.slide = function (type, next) { ++ var $active = this.$element.find('.item.active') ++ var $next = next || this.getItemForDirection(type, $active) ++ var isCycling = this.interval ++ var direction = type == 'next' ? 'left' : 'right' ++ var that = this ++ ++ if ($next.hasClass('active')) return (this.sliding = false) ++ ++ var relatedTarget = $next[0] ++ var slideEvent = $.Event('slide.bs.carousel', { ++ relatedTarget: relatedTarget, ++ direction: direction ++ }) ++ this.$element.trigger(slideEvent) ++ if (slideEvent.isDefaultPrevented()) return ++ ++ this.sliding = true ++ ++ isCycling && this.pause() ++ ++ if (this.$indicators.length) { ++ this.$indicators.find('.active').removeClass('active') ++ var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) ++ $nextIndicator && $nextIndicator.addClass('active') ++ } ++ ++ var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" ++ if ($.support.transition && this.$element.hasClass('slide')) { ++ $next.addClass(type) ++ $next[0].offsetWidth // force reflow ++ $active.addClass(direction) ++ $next.addClass(direction) ++ $active ++ .one('bsTransitionEnd', function () { ++ $next.removeClass([type, direction].join(' ')).addClass('active') ++ $active.removeClass(['active', direction].join(' ')) ++ that.sliding = false ++ setTimeout(function () { ++ that.$element.trigger(slidEvent) ++ }, 0) ++ }) ++ .emulateTransitionEnd(Carousel.TRANSITION_DURATION) ++ } else { ++ $active.removeClass('active') ++ $next.addClass('active') ++ this.sliding = false ++ this.$element.trigger(slidEvent) ++ } ++ ++ isCycling && this.cycle() ++ ++ return this ++ } ++ ++ ++ // CAROUSEL PLUGIN DEFINITION ++ // ========================== ++ ++ function Plugin(option) { ++ return this.each(function () { ++ var $this = $(this) ++ var data = $this.data('bs.carousel') ++ var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) ++ var action = typeof option == 'string' ? option : options.slide ++ ++ if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) ++ if (typeof option == 'number') data.to(option) ++ else if (action) data[action]() ++ else if (options.interval) data.pause().cycle() ++ }) ++ } ++ ++ var old = $.fn.carousel ++ ++ $.fn.carousel = Plugin ++ $.fn.carousel.Constructor = Carousel ++ ++ ++ // CAROUSEL NO CONFLICT ++ // ==================== ++ ++ $.fn.carousel.noConflict = function () { ++ $.fn.carousel = old ++ return this ++ } ++ ++ ++ // CAROUSEL DATA-API ++ // ================= ++ ++ var clickHandler = function (e) { ++ var href ++ var $this = $(this) ++ var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 ++ if (!$target.hasClass('carousel')) return ++ var options = $.extend({}, $target.data(), $this.data()) ++ var slideIndex = $this.attr('data-slide-to') ++ if (slideIndex) options.interval = false ++ ++ Plugin.call($target, options) ++ ++ if (slideIndex) { ++ $target.data('bs.carousel').to(slideIndex) ++ } ++ ++ e.preventDefault() ++ } ++ ++ $(document) ++ .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) ++ .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) ++ ++ $(window).on('load', function () { ++ $('[data-ride="carousel"]').each(function () { ++ var $carousel = $(this) ++ Plugin.call($carousel, $carousel.data()) ++ }) ++ }) ++ ++}(jQuery); ++ ++/* ======================================================================== ++ * Bootstrap: collapse.js v3.3.7 ++ * http://getbootstrap.com/javascript/#collapse ++ * ======================================================================== ++ * Copyright 2011-2016 Twitter, Inc. ++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) ++ * ======================================================================== */ ++ ++/* jshint latedef: false */ ++ +++function ($) { ++ 'use strict'; ++ ++ // COLLAPSE PUBLIC CLASS DEFINITION ++ // ================================ ++ ++ var Collapse = function (element, options) { ++ this.$element = $(element) ++ this.options = $.extend({}, Collapse.DEFAULTS, options) ++ this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' + ++ '[data-toggle="collapse"][data-target="#' + element.id + '"]') ++ this.transitioning = null ++ ++ if (this.options.parent) { ++ this.$parent = this.getParent() ++ } else { ++ this.addAriaAndCollapsedClass(this.$element, this.$trigger) ++ } ++ ++ if (this.options.toggle) this.toggle() ++ } ++ ++ Collapse.VERSION = '3.3.7' ++ ++ Collapse.TRANSITION_DURATION = 350 ++ ++ Collapse.DEFAULTS = { ++ toggle: true ++ } ++ ++ Collapse.prototype.dimension = function () { ++ var hasWidth = this.$element.hasClass('width') ++ return hasWidth ? 'width' : 'height' ++ } ++ ++ Collapse.prototype.show = function () { ++ if (this.transitioning || this.$element.hasClass('in')) return ++ ++ var activesData ++ var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing') ++ ++ if (actives && actives.length) { ++ activesData = actives.data('bs.collapse') ++ if (activesData && activesData.transitioning) return ++ } ++ ++ var startEvent = $.Event('show.bs.collapse') ++ this.$element.trigger(startEvent) ++ if (startEvent.isDefaultPrevented()) return ++ ++ if (actives && actives.length) { ++ Plugin.call(actives, 'hide') ++ activesData || actives.data('bs.collapse', null) ++ } ++ ++ var dimension = this.dimension() ++ ++ this.$element ++ .removeClass('collapse') ++ .addClass('collapsing')[dimension](0) ++ .attr('aria-expanded', true) ++ ++ this.$trigger ++ .removeClass('collapsed') ++ .attr('aria-expanded', true) ++ ++ this.transitioning = 1 ++ ++ var complete = function () { ++ this.$element ++ .removeClass('collapsing') ++ .addClass('collapse in')[dimension]('') ++ this.transitioning = 0 ++ this.$element ++ .trigger('shown.bs.collapse') ++ } ++ ++ if (!$.support.transition) return complete.call(this) ++ ++ var scrollSize = $.camelCase(['scroll', dimension].join('-')) ++ ++ this.$element ++ .one('bsTransitionEnd', $.proxy(complete, this)) ++ .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) ++ } ++ ++ Collapse.prototype.hide = function () { ++ if (this.transitioning || !this.$element.hasClass('in')) return ++ ++ var startEvent = $.Event('hide.bs.collapse') ++ this.$element.trigger(startEvent) ++ if (startEvent.isDefaultPrevented()) return ++ ++ var dimension = this.dimension() ++ ++ this.$element[dimension](this.$element[dimension]())[0].offsetHeight ++ ++ this.$element ++ .addClass('collapsing') ++ .removeClass('collapse in') ++ .attr('aria-expanded', false) ++ ++ this.$trigger ++ .addClass('collapsed') ++ .attr('aria-expanded', false) ++ ++ this.transitioning = 1 ++ ++ var complete = function () { ++ this.transitioning = 0 ++ this.$element ++ .removeClass('collapsing') ++ .addClass('collapse') ++ .trigger('hidden.bs.collapse') ++ } ++ ++ if (!$.support.transition) return complete.call(this) ++ ++ this.$element ++ [dimension](0) ++ .one('bsTransitionEnd', $.proxy(complete, this)) ++ .emulateTransitionEnd(Collapse.TRANSITION_DURATION) ++ } ++ ++ Collapse.prototype.toggle = function () { ++ this[this.$element.hasClass('in') ? 'hide' : 'show']() ++ } ++ ++ Collapse.prototype.getParent = function () { ++ return $(this.options.parent) ++ .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') ++ .each($.proxy(function (i, element) { ++ var $element = $(element) ++ this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) ++ }, this)) ++ .end() ++ } ++ ++ Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { ++ var isOpen = $element.hasClass('in') ++ ++ $element.attr('aria-expanded', isOpen) ++ $trigger ++ .toggleClass('collapsed', !isOpen) ++ .attr('aria-expanded', isOpen) ++ } ++ ++ function getTargetFromTrigger($trigger) { ++ var href ++ var target = $trigger.attr('data-target') ++ || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 ++ ++ return $(target) ++ } ++ ++ ++ // COLLAPSE PLUGIN DEFINITION ++ // ========================== ++ ++ function Plugin(option) { ++ return this.each(function () { ++ var $this = $(this) ++ var data = $this.data('bs.collapse') ++ var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) ++ ++ if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false ++ if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) ++ if (typeof option == 'string') data[option]() ++ }) ++ } ++ ++ var old = $.fn.collapse ++ ++ $.fn.collapse = Plugin ++ $.fn.collapse.Constructor = Collapse ++ ++ ++ // COLLAPSE NO CONFLICT ++ // ==================== ++ ++ $.fn.collapse.noConflict = function () { ++ $.fn.collapse = old ++ return this ++ } ++ ++ ++ // COLLAPSE DATA-API ++ // ================= ++ ++ $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { ++ var $this = $(this) ++ ++ if (!$this.attr('data-target')) e.preventDefault() ++ ++ var $target = getTargetFromTrigger($this) ++ var data = $target.data('bs.collapse') ++ var option = data ? 'toggle' : $this.data() ++ ++ Plugin.call($target, option) ++ }) ++ ++}(jQuery); ++ ++/* ======================================================================== ++ * Bootstrap: dropdown.js v3.3.7 ++ * http://getbootstrap.com/javascript/#dropdowns ++ * ======================================================================== ++ * Copyright 2011-2016 Twitter, Inc. ++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) ++ * ======================================================================== */ ++ ++ +++function ($) { ++ 'use strict'; ++ ++ // DROPDOWN CLASS DEFINITION ++ // ========================= ++ ++ var backdrop = '.dropdown-backdrop' ++ var toggle = '[data-toggle="dropdown"]' ++ var Dropdown = function (element) { ++ $(element).on('click.bs.dropdown', this.toggle) ++ } ++ ++ Dropdown.VERSION = '3.3.7' ++ ++ function getParent($this) { ++ var selector = $this.attr('data-target') ++ ++ if (!selector) { ++ selector = $this.attr('href') ++ selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 ++ } ++ ++ var $parent = selector && $(selector) ++ ++ return $parent && $parent.length ? $parent : $this.parent() ++ } ++ ++ function clearMenus(e) { ++ if (e && e.which === 3) return ++ $(backdrop).remove() ++ $(toggle).each(function () { ++ var $this = $(this) ++ var $parent = getParent($this) ++ var relatedTarget = { relatedTarget: this } ++ ++ if (!$parent.hasClass('open')) return ++ ++ if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return ++ ++ $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)) ++ ++ if (e.isDefaultPrevented()) return ++ ++ $this.attr('aria-expanded', 'false') ++ $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget)) ++ }) ++ } ++ ++ Dropdown.prototype.toggle = function (e) { ++ var $this = $(this) ++ ++ if ($this.is('.disabled, :disabled')) return ++ ++ var $parent = getParent($this) ++ var isActive = $parent.hasClass('open') ++ ++ clearMenus() ++ ++ if (!isActive) { ++ if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { ++ // if mobile we use a backdrop because click events don't delegate ++ $(document.createElement('div')) ++ .addClass('dropdown-backdrop') ++ .insertAfter($(this)) ++ .on('click', clearMenus) ++ } ++ ++ var relatedTarget = { relatedTarget: this } ++ $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget)) ++ ++ if (e.isDefaultPrevented()) return ++ ++ $this ++ .trigger('focus') ++ .attr('aria-expanded', 'true') ++ ++ $parent ++ .toggleClass('open') ++ .trigger($.Event('shown.bs.dropdown', relatedTarget)) ++ } ++ ++ return false ++ } ++ ++ Dropdown.prototype.keydown = function (e) { ++ if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return ++ ++ var $this = $(this) ++ ++ e.preventDefault() ++ e.stopPropagation() ++ ++ if ($this.is('.disabled, :disabled')) return ++ ++ var $parent = getParent($this) ++ var isActive = $parent.hasClass('open') ++ ++ if (!isActive && e.which != 27 || isActive && e.which == 27) { ++ if (e.which == 27) $parent.find(toggle).trigger('focus') ++ return $this.trigger('click') ++ } ++ ++ var desc = ' li:not(.disabled):visible a' ++ var $items = $parent.find('.dropdown-menu' + desc) ++ ++ if (!$items.length) return ++ ++ var index = $items.index(e.target) ++ ++ if (e.which == 38 && index > 0) index-- // up ++ if (e.which == 40 && index < $items.length - 1) index++ // down ++ if (!~index) index = 0 ++ ++ $items.eq(index).trigger('focus') ++ } ++ ++ ++ // DROPDOWN PLUGIN DEFINITION ++ // ========================== ++ ++ function Plugin(option) { ++ return this.each(function () { ++ var $this = $(this) ++ var data = $this.data('bs.dropdown') ++ ++ if (!data) $this.data('bs.dropdown', (data = new Dropdown(this))) ++ if (typeof option == 'string') data[option].call($this) ++ }) ++ } ++ ++ var old = $.fn.dropdown ++ ++ $.fn.dropdown = Plugin ++ $.fn.dropdown.Constructor = Dropdown ++ ++ ++ // DROPDOWN NO CONFLICT ++ // ==================== ++ ++ $.fn.dropdown.noConflict = function () { ++ $.fn.dropdown = old ++ return this ++ } ++ ++ ++ // APPLY TO STANDARD DROPDOWN ELEMENTS ++ // =================================== ++ ++ $(document) ++ .on('click.bs.dropdown.data-api', clearMenus) ++ .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) ++ .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle) ++ .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown) ++ .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown) ++ ++}(jQuery); ++ ++/* ======================================================================== ++ * Bootstrap: modal.js v3.3.7 ++ * http://getbootstrap.com/javascript/#modals ++ * ======================================================================== ++ * Copyright 2011-2016 Twitter, Inc. ++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) ++ * ======================================================================== */ ++ ++ +++function ($) { ++ 'use strict'; ++ ++ // MODAL CLASS DEFINITION ++ // ====================== ++ ++ var Modal = function (element, options) { ++ this.options = options ++ this.$body = $(document.body) ++ this.$element = $(element) ++ this.$dialog = this.$element.find('.modal-dialog') ++ this.$backdrop = null ++ this.isShown = null ++ this.originalBodyPad = null ++ this.scrollbarWidth = 0 ++ this.ignoreBackdropClick = false ++ ++ if (this.options.remote) { ++ this.$element ++ .find('.modal-content') ++ .load(this.options.remote, $.proxy(function () { ++ this.$element.trigger('loaded.bs.modal') ++ }, this)) ++ } ++ } ++ ++ Modal.VERSION = '3.3.7' ++ ++ Modal.TRANSITION_DURATION = 300 ++ Modal.BACKDROP_TRANSITION_DURATION = 150 ++ ++ Modal.DEFAULTS = { ++ backdrop: true, ++ keyboard: true, ++ show: true ++ } ++ ++ Modal.prototype.toggle = function (_relatedTarget) { ++ return this.isShown ? this.hide() : this.show(_relatedTarget) ++ } ++ ++ Modal.prototype.show = function (_relatedTarget) { ++ var that = this ++ var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget }) ++ ++ this.$element.trigger(e) ++ ++ if (this.isShown || e.isDefaultPrevented()) return ++ ++ this.isShown = true ++ ++ this.checkScrollbar() ++ this.setScrollbar() ++ this.$body.addClass('modal-open') ++ ++ this.escape() ++ this.resize() ++ ++ this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this)) ++ ++ this.$dialog.on('mousedown.dismiss.bs.modal', function () { ++ that.$element.one('mouseup.dismiss.bs.modal', function (e) { ++ if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true ++ }) ++ }) ++ ++ this.backdrop(function () { ++ var transition = $.support.transition && that.$element.hasClass('fade') ++ ++ if (!that.$element.parent().length) { ++ that.$element.appendTo(that.$body) // don't move modals dom position ++ } ++ ++ that.$element ++ .show() ++ .scrollTop(0) ++ ++ that.adjustDialog() ++ ++ if (transition) { ++ that.$element[0].offsetWidth // force reflow ++ } ++ ++ that.$element.addClass('in') ++ ++ that.enforceFocus() ++ ++ var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget }) ++ ++ transition ? ++ that.$dialog // wait for modal to slide in ++ .one('bsTransitionEnd', function () { ++ that.$element.trigger('focus').trigger(e) ++ }) ++ .emulateTransitionEnd(Modal.TRANSITION_DURATION) : ++ that.$element.trigger('focus').trigger(e) ++ }) ++ } ++ ++ Modal.prototype.hide = function (e) { ++ if (e) e.preventDefault() ++ ++ e = $.Event('hide.bs.modal') ++ ++ this.$element.trigger(e) ++ ++ if (!this.isShown || e.isDefaultPrevented()) return ++ ++ this.isShown = false ++ ++ this.escape() ++ this.resize() ++ ++ $(document).off('focusin.bs.modal') ++ ++ this.$element ++ .removeClass('in') ++ .off('click.dismiss.bs.modal') ++ .off('mouseup.dismiss.bs.modal') ++ ++ this.$dialog.off('mousedown.dismiss.bs.modal') ++ ++ $.support.transition && this.$element.hasClass('fade') ? ++ this.$element ++ .one('bsTransitionEnd', $.proxy(this.hideModal, this)) ++ .emulateTransitionEnd(Modal.TRANSITION_DURATION) : ++ this.hideModal() ++ } ++ ++ Modal.prototype.enforceFocus = function () { ++ $(document) ++ .off('focusin.bs.modal') // guard against infinite focus loop ++ .on('focusin.bs.modal', $.proxy(function (e) { ++ if (document !== e.target && ++ this.$element[0] !== e.target && ++ !this.$element.has(e.target).length) { ++ this.$element.trigger('focus') ++ } ++ }, this)) ++ } ++ ++ Modal.prototype.escape = function () { ++ if (this.isShown && this.options.keyboard) { ++ this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) { ++ e.which == 27 && this.hide() ++ }, this)) ++ } else if (!this.isShown) { ++ this.$element.off('keydown.dismiss.bs.modal') ++ } ++ } ++ ++ Modal.prototype.resize = function () { ++ if (this.isShown) { ++ $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this)) ++ } else { ++ $(window).off('resize.bs.modal') ++ } ++ } ++ ++ Modal.prototype.hideModal = function () { ++ var that = this ++ this.$element.hide() ++ this.backdrop(function () { ++ that.$body.removeClass('modal-open') ++ that.resetAdjustments() ++ that.resetScrollbar() ++ that.$element.trigger('hidden.bs.modal') ++ }) ++ } ++ ++ Modal.prototype.removeBackdrop = function () { ++ this.$backdrop && this.$backdrop.remove() ++ this.$backdrop = null ++ } ++ ++ Modal.prototype.backdrop = function (callback) { ++ var that = this ++ var animate = this.$element.hasClass('fade') ? 'fade' : '' ++ ++ if (this.isShown && this.options.backdrop) { ++ var doAnimate = $.support.transition && animate ++ ++ this.$backdrop = $(document.createElement('div')) ++ .addClass('modal-backdrop ' + animate) ++ .appendTo(this.$body) ++ ++ this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) { ++ if (this.ignoreBackdropClick) { ++ this.ignoreBackdropClick = false ++ return ++ } ++ if (e.target !== e.currentTarget) return ++ this.options.backdrop == 'static' ++ ? this.$element[0].focus() ++ : this.hide() ++ }, this)) ++ ++ if (doAnimate) this.$backdrop[0].offsetWidth // force reflow ++ ++ this.$backdrop.addClass('in') ++ ++ if (!callback) return ++ ++ doAnimate ? ++ this.$backdrop ++ .one('bsTransitionEnd', callback) ++ .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : ++ callback() ++ ++ } else if (!this.isShown && this.$backdrop) { ++ this.$backdrop.removeClass('in') ++ ++ var callbackRemove = function () { ++ that.removeBackdrop() ++ callback && callback() ++ } ++ $.support.transition && this.$element.hasClass('fade') ? ++ this.$backdrop ++ .one('bsTransitionEnd', callbackRemove) ++ .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : ++ callbackRemove() ++ ++ } else if (callback) { ++ callback() ++ } ++ } ++ ++ // these following methods are used to handle overflowing modals ++ ++ Modal.prototype.handleUpdate = function () { ++ this.adjustDialog() ++ } ++ ++ Modal.prototype.adjustDialog = function () { ++ var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight ++ ++ this.$element.css({ ++ paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '', ++ paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : '' ++ }) ++ } ++ ++ Modal.prototype.resetAdjustments = function () { ++ this.$element.css({ ++ paddingLeft: '', ++ paddingRight: '' ++ }) ++ } ++ ++ Modal.prototype.checkScrollbar = function () { ++ var fullWindowWidth = window.innerWidth ++ if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8 ++ var documentElementRect = document.documentElement.getBoundingClientRect() ++ fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left) ++ } ++ this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth ++ this.scrollbarWidth = this.measureScrollbar() ++ } ++ ++ Modal.prototype.setScrollbar = function () { ++ var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10) ++ this.originalBodyPad = document.body.style.paddingRight || '' ++ if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth) ++ } ++ ++ Modal.prototype.resetScrollbar = function () { ++ this.$body.css('padding-right', this.originalBodyPad) ++ } ++ ++ Modal.prototype.measureScrollbar = function () { // thx walsh ++ var scrollDiv = document.createElement('div') ++ scrollDiv.className = 'modal-scrollbar-measure' ++ this.$body.append(scrollDiv) ++ var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth ++ this.$body[0].removeChild(scrollDiv) ++ return scrollbarWidth ++ } ++ ++ ++ // MODAL PLUGIN DEFINITION ++ // ======================= ++ ++ function Plugin(option, _relatedTarget) { ++ return this.each(function () { ++ var $this = $(this) ++ var data = $this.data('bs.modal') ++ var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option) ++ ++ if (!data) $this.data('bs.modal', (data = new Modal(this, options))) ++ if (typeof option == 'string') data[option](_relatedTarget) ++ else if (options.show) data.show(_relatedTarget) ++ }) ++ } ++ ++ var old = $.fn.modal ++ ++ $.fn.modal = Plugin ++ $.fn.modal.Constructor = Modal ++ ++ ++ // MODAL NO CONFLICT ++ // ================= ++ ++ $.fn.modal.noConflict = function () { ++ $.fn.modal = old ++ return this ++ } ++ ++ ++ // MODAL DATA-API ++ // ============== ++ ++ $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) { ++ var $this = $(this) ++ var href = $this.attr('href') ++ var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7 ++ var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data()) ++ ++ if ($this.is('a')) e.preventDefault() ++ ++ $target.one('show.bs.modal', function (showEvent) { ++ if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown ++ $target.one('hidden.bs.modal', function () { ++ $this.is(':visible') && $this.trigger('focus') ++ }) ++ }) ++ Plugin.call($target, option, this) ++ }) ++ ++}(jQuery); ++ ++/* ======================================================================== ++ * Bootstrap: tooltip.js v3.3.7 ++ * http://getbootstrap.com/javascript/#tooltip ++ * Inspired by the original jQuery.tipsy by Jason Frame ++ * ======================================================================== ++ * Copyright 2011-2016 Twitter, Inc. ++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) ++ * ======================================================================== */ ++ ++ +++function ($) { ++ 'use strict'; ++ ++ // TOOLTIP PUBLIC CLASS DEFINITION ++ // =============================== ++ ++ var Tooltip = function (element, options) { ++ this.type = null ++ this.options = null ++ this.enabled = null ++ this.timeout = null ++ this.hoverState = null ++ this.$element = null ++ this.inState = null ++ ++ this.init('tooltip', element, options) ++ } ++ ++ Tooltip.VERSION = '3.3.7' ++ ++ Tooltip.TRANSITION_DURATION = 150 ++ ++ Tooltip.DEFAULTS = { ++ animation: true, ++ placement: 'top', ++ selector: false, ++ template: '', ++ trigger: 'hover focus', ++ title: '', ++ delay: 0, ++ html: false, ++ container: false, ++ viewport: { ++ selector: 'body', ++ padding: 0 ++ } ++ } ++ ++ Tooltip.prototype.init = function (type, element, options) { ++ this.enabled = true ++ this.type = type ++ this.$element = $(element) ++ this.options = this.getOptions(options) ++ this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport)) ++ this.inState = { click: false, hover: false, focus: false } ++ ++ if (this.$element[0] instanceof document.constructor && !this.options.selector) { ++ throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!') ++ } ++ ++ var triggers = this.options.trigger.split(' ') ++ ++ for (var i = triggers.length; i--;) { ++ var trigger = triggers[i] ++ ++ if (trigger == 'click') { ++ this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) ++ } else if (trigger != 'manual') { ++ var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin' ++ var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout' ++ ++ this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) ++ this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) ++ } ++ } ++ ++ this.options.selector ? ++ (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : ++ this.fixTitle() ++ } ++ ++ Tooltip.prototype.getDefaults = function () { ++ return Tooltip.DEFAULTS ++ } ++ ++ Tooltip.prototype.getOptions = function (options) { ++ options = $.extend({}, this.getDefaults(), this.$element.data(), options) ++ ++ if (options.delay && typeof options.delay == 'number') { ++ options.delay = { ++ show: options.delay, ++ hide: options.delay ++ } ++ } ++ ++ return options ++ } ++ ++ Tooltip.prototype.getDelegateOptions = function () { ++ var options = {} ++ var defaults = this.getDefaults() ++ ++ this._options && $.each(this._options, function (key, value) { ++ if (defaults[key] != value) options[key] = value ++ }) ++ ++ return options ++ } ++ ++ Tooltip.prototype.enter = function (obj) { ++ var self = obj instanceof this.constructor ? ++ obj : $(obj.currentTarget).data('bs.' + this.type) ++ ++ if (!self) { ++ self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) ++ $(obj.currentTarget).data('bs.' + this.type, self) ++ } ++ ++ if (obj instanceof $.Event) { ++ self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true ++ } ++ ++ if (self.tip().hasClass('in') || self.hoverState == 'in') { ++ self.hoverState = 'in' ++ return ++ } ++ ++ clearTimeout(self.timeout) ++ ++ self.hoverState = 'in' ++ ++ if (!self.options.delay || !self.options.delay.show) return self.show() ++ ++ self.timeout = setTimeout(function () { ++ if (self.hoverState == 'in') self.show() ++ }, self.options.delay.show) ++ } ++ ++ Tooltip.prototype.isInStateTrue = function () { ++ for (var key in this.inState) { ++ if (this.inState[key]) return true ++ } ++ ++ return false ++ } ++ ++ Tooltip.prototype.leave = function (obj) { ++ var self = obj instanceof this.constructor ? ++ obj : $(obj.currentTarget).data('bs.' + this.type) ++ ++ if (!self) { ++ self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) ++ $(obj.currentTarget).data('bs.' + this.type, self) ++ } ++ ++ if (obj instanceof $.Event) { ++ self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false ++ } ++ ++ if (self.isInStateTrue()) return ++ ++ clearTimeout(self.timeout) ++ ++ self.hoverState = 'out' ++ ++ if (!self.options.delay || !self.options.delay.hide) return self.hide() ++ ++ self.timeout = setTimeout(function () { ++ if (self.hoverState == 'out') self.hide() ++ }, self.options.delay.hide) ++ } ++ ++ Tooltip.prototype.show = function () { ++ var e = $.Event('show.bs.' + this.type) ++ ++ if (this.hasContent() && this.enabled) { ++ this.$element.trigger(e) ++ ++ var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]) ++ if (e.isDefaultPrevented() || !inDom) return ++ var that = this ++ ++ var $tip = this.tip() ++ ++ var tipId = this.getUID(this.type) ++ ++ this.setContent() ++ $tip.attr('id', tipId) ++ this.$element.attr('aria-describedby', tipId) ++ ++ if (this.options.animation) $tip.addClass('fade') ++ ++ var placement = typeof this.options.placement == 'function' ? ++ this.options.placement.call(this, $tip[0], this.$element[0]) : ++ this.options.placement ++ ++ var autoToken = /\s?auto?\s?/i ++ var autoPlace = autoToken.test(placement) ++ if (autoPlace) placement = placement.replace(autoToken, '') || 'top' ++ ++ $tip ++ .detach() ++ .css({ top: 0, left: 0, display: 'block' }) ++ .addClass(placement) ++ .data('bs.' + this.type, this) ++ ++ this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) ++ this.$element.trigger('inserted.bs.' + this.type) ++ ++ var pos = this.getPosition() ++ var actualWidth = $tip[0].offsetWidth ++ var actualHeight = $tip[0].offsetHeight ++ ++ if (autoPlace) { ++ var orgPlacement = placement ++ var viewportDim = this.getPosition(this.$viewport) ++ ++ placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' : ++ placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' : ++ placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' : ++ placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' : ++ placement ++ ++ $tip ++ .removeClass(orgPlacement) ++ .addClass(placement) ++ } ++ ++ var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight) ++ ++ this.applyPlacement(calculatedOffset, placement) ++ ++ var complete = function () { ++ var prevHoverState = that.hoverState ++ that.$element.trigger('shown.bs.' + that.type) ++ that.hoverState = null ++ ++ if (prevHoverState == 'out') that.leave(that) ++ } ++ ++ $.support.transition && this.$tip.hasClass('fade') ? ++ $tip ++ .one('bsTransitionEnd', complete) ++ .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : ++ complete() ++ } ++ } ++ ++ Tooltip.prototype.applyPlacement = function (offset, placement) { ++ var $tip = this.tip() ++ var width = $tip[0].offsetWidth ++ var height = $tip[0].offsetHeight ++ ++ // manually read margins because getBoundingClientRect includes difference ++ var marginTop = parseInt($tip.css('margin-top'), 10) ++ var marginLeft = parseInt($tip.css('margin-left'), 10) ++ ++ // we must check for NaN for ie 8/9 ++ if (isNaN(marginTop)) marginTop = 0 ++ if (isNaN(marginLeft)) marginLeft = 0 ++ ++ offset.top += marginTop ++ offset.left += marginLeft ++ ++ // $.fn.offset doesn't round pixel values ++ // so we use setOffset directly with our own function B-0 ++ $.offset.setOffset($tip[0], $.extend({ ++ using: function (props) { ++ $tip.css({ ++ top: Math.round(props.top), ++ left: Math.round(props.left) ++ }) ++ } ++ }, offset), 0) ++ ++ $tip.addClass('in') ++ ++ // check to see if placing tip in new offset caused the tip to resize itself ++ var actualWidth = $tip[0].offsetWidth ++ var actualHeight = $tip[0].offsetHeight ++ ++ if (placement == 'top' && actualHeight != height) { ++ offset.top = offset.top + height - actualHeight ++ } ++ ++ var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight) ++ ++ if (delta.left) offset.left += delta.left ++ else offset.top += delta.top ++ ++ var isVertical = /top|bottom/.test(placement) ++ var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight ++ var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight' ++ ++ $tip.offset(offset) ++ this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical) ++ } ++ ++ Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) { ++ this.arrow() ++ .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%') ++ .css(isVertical ? 'top' : 'left', '') ++ } ++ ++ Tooltip.prototype.setContent = function () { ++ var $tip = this.tip() ++ var title = this.getTitle() ++ ++ $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) ++ $tip.removeClass('fade in top bottom left right') ++ } ++ ++ Tooltip.prototype.hide = function (callback) { ++ var that = this ++ var $tip = $(this.$tip) ++ var e = $.Event('hide.bs.' + this.type) ++ ++ function complete() { ++ if (that.hoverState != 'in') $tip.detach() ++ if (that.$element) { // TODO: Check whether guarding this code with this `if` is really necessary. ++ that.$element ++ .removeAttr('aria-describedby') ++ .trigger('hidden.bs.' + that.type) ++ } ++ callback && callback() ++ } ++ ++ this.$element.trigger(e) ++ ++ if (e.isDefaultPrevented()) return ++ ++ $tip.removeClass('in') ++ ++ $.support.transition && $tip.hasClass('fade') ? ++ $tip ++ .one('bsTransitionEnd', complete) ++ .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : ++ complete() ++ ++ this.hoverState = null ++ ++ return this ++ } ++ ++ Tooltip.prototype.fixTitle = function () { ++ var $e = this.$element ++ if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') { ++ $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') ++ } ++ } ++ ++ Tooltip.prototype.hasContent = function () { ++ return this.getTitle() ++ } ++ ++ Tooltip.prototype.getPosition = function ($element) { ++ $element = $element || this.$element ++ ++ var el = $element[0] ++ var isBody = el.tagName == 'BODY' ++ ++ var elRect = el.getBoundingClientRect() ++ if (elRect.width == null) { ++ // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093 ++ elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top }) ++ } ++ var isSvg = window.SVGElement && el instanceof window.SVGElement ++ // Avoid using $.offset() on SVGs since it gives incorrect results in jQuery 3. ++ // See https://github.com/twbs/bootstrap/issues/20280 ++ var elOffset = isBody ? { top: 0, left: 0 } : (isSvg ? null : $element.offset()) ++ var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() } ++ var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null ++ ++ return $.extend({}, elRect, scroll, outerDims, elOffset) ++ } ++ ++ Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { ++ return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : ++ placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : ++ placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : ++ /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } ++ ++ } ++ ++ Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) { ++ var delta = { top: 0, left: 0 } ++ if (!this.$viewport) return delta ++ ++ var viewportPadding = this.options.viewport && this.options.viewport.padding || 0 ++ var viewportDimensions = this.getPosition(this.$viewport) ++ ++ if (/right|left/.test(placement)) { ++ var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll ++ var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight ++ if (topEdgeOffset < viewportDimensions.top) { // top overflow ++ delta.top = viewportDimensions.top - topEdgeOffset ++ } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow ++ delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset ++ } ++ } else { ++ var leftEdgeOffset = pos.left - viewportPadding ++ var rightEdgeOffset = pos.left + viewportPadding + actualWidth ++ if (leftEdgeOffset < viewportDimensions.left) { // left overflow ++ delta.left = viewportDimensions.left - leftEdgeOffset ++ } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow ++ delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset ++ } ++ } ++ ++ return delta ++ } ++ ++ Tooltip.prototype.getTitle = function () { ++ var title ++ var $e = this.$element ++ var o = this.options ++ ++ title = $e.attr('data-original-title') ++ || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) ++ ++ return title ++ } ++ ++ Tooltip.prototype.getUID = function (prefix) { ++ do prefix += ~~(Math.random() * 1000000) ++ while (document.getElementById(prefix)) ++ return prefix ++ } ++ ++ Tooltip.prototype.tip = function () { ++ if (!this.$tip) { ++ this.$tip = $(this.options.template) ++ if (this.$tip.length != 1) { ++ throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!') ++ } ++ } ++ return this.$tip ++ } ++ ++ Tooltip.prototype.arrow = function () { ++ return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')) ++ } ++ ++ Tooltip.prototype.enable = function () { ++ this.enabled = true ++ } ++ ++ Tooltip.prototype.disable = function () { ++ this.enabled = false ++ } ++ ++ Tooltip.prototype.toggleEnabled = function () { ++ this.enabled = !this.enabled ++ } ++ ++ Tooltip.prototype.toggle = function (e) { ++ var self = this ++ if (e) { ++ self = $(e.currentTarget).data('bs.' + this.type) ++ if (!self) { ++ self = new this.constructor(e.currentTarget, this.getDelegateOptions()) ++ $(e.currentTarget).data('bs.' + this.type, self) ++ } ++ } ++ ++ if (e) { ++ self.inState.click = !self.inState.click ++ if (self.isInStateTrue()) self.enter(self) ++ else self.leave(self) ++ } else { ++ self.tip().hasClass('in') ? self.leave(self) : self.enter(self) ++ } ++ } ++ ++ Tooltip.prototype.destroy = function () { ++ var that = this ++ clearTimeout(this.timeout) ++ this.hide(function () { ++ that.$element.off('.' + that.type).removeData('bs.' + that.type) ++ if (that.$tip) { ++ that.$tip.detach() ++ } ++ that.$tip = null ++ that.$arrow = null ++ that.$viewport = null ++ that.$element = null ++ }) ++ } ++ ++ ++ // TOOLTIP PLUGIN DEFINITION ++ // ========================= ++ ++ function Plugin(option) { ++ return this.each(function () { ++ var $this = $(this) ++ var data = $this.data('bs.tooltip') ++ var options = typeof option == 'object' && option ++ ++ if (!data && /destroy|hide/.test(option)) return ++ if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) ++ if (typeof option == 'string') data[option]() ++ }) ++ } ++ ++ var old = $.fn.tooltip ++ ++ $.fn.tooltip = Plugin ++ $.fn.tooltip.Constructor = Tooltip ++ ++ ++ // TOOLTIP NO CONFLICT ++ // =================== ++ ++ $.fn.tooltip.noConflict = function () { ++ $.fn.tooltip = old ++ return this ++ } ++ ++}(jQuery); ++ ++/* ======================================================================== ++ * Bootstrap: popover.js v3.3.7 ++ * http://getbootstrap.com/javascript/#popovers ++ * ======================================================================== ++ * Copyright 2011-2016 Twitter, Inc. ++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) ++ * ======================================================================== */ ++ ++ +++function ($) { ++ 'use strict'; ++ ++ // POPOVER PUBLIC CLASS DEFINITION ++ // =============================== ++ ++ var Popover = function (element, options) { ++ this.init('popover', element, options) ++ } ++ ++ if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js') ++ ++ Popover.VERSION = '3.3.7' ++ ++ Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, { ++ placement: 'right', ++ trigger: 'click', ++ content: '', ++ template: '' ++ }) ++ ++ ++ // NOTE: POPOVER EXTENDS tooltip.js ++ // ================================ ++ ++ Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype) ++ ++ Popover.prototype.constructor = Popover ++ ++ Popover.prototype.getDefaults = function () { ++ return Popover.DEFAULTS ++ } ++ ++ Popover.prototype.setContent = function () { ++ var $tip = this.tip() ++ var title = this.getTitle() ++ var content = this.getContent() ++ ++ $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) ++ $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events ++ this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text' ++ ](content) ++ ++ $tip.removeClass('fade top bottom left right in') ++ ++ // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do ++ // this manually by checking the contents. ++ if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide() ++ } ++ ++ Popover.prototype.hasContent = function () { ++ return this.getTitle() || this.getContent() ++ } ++ ++ Popover.prototype.getContent = function () { ++ var $e = this.$element ++ var o = this.options ++ ++ return $e.attr('data-content') ++ || (typeof o.content == 'function' ? ++ o.content.call($e[0]) : ++ o.content) ++ } ++ ++ Popover.prototype.arrow = function () { ++ return (this.$arrow = this.$arrow || this.tip().find('.arrow')) ++ } ++ ++ ++ // POPOVER PLUGIN DEFINITION ++ // ========================= ++ ++ function Plugin(option) { ++ return this.each(function () { ++ var $this = $(this) ++ var data = $this.data('bs.popover') ++ var options = typeof option == 'object' && option ++ ++ if (!data && /destroy|hide/.test(option)) return ++ if (!data) $this.data('bs.popover', (data = new Popover(this, options))) ++ if (typeof option == 'string') data[option]() ++ }) ++ } ++ ++ var old = $.fn.popover ++ ++ $.fn.popover = Plugin ++ $.fn.popover.Constructor = Popover ++ ++ ++ // POPOVER NO CONFLICT ++ // =================== ++ ++ $.fn.popover.noConflict = function () { ++ $.fn.popover = old ++ return this ++ } ++ ++}(jQuery); ++ ++/* ======================================================================== ++ * Bootstrap: scrollspy.js v3.3.7 ++ * http://getbootstrap.com/javascript/#scrollspy ++ * ======================================================================== ++ * Copyright 2011-2016 Twitter, Inc. ++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) ++ * ======================================================================== */ ++ ++ +++function ($) { ++ 'use strict'; ++ ++ // SCROLLSPY CLASS DEFINITION ++ // ========================== ++ ++ function ScrollSpy(element, options) { ++ this.$body = $(document.body) ++ this.$scrollElement = $(element).is(document.body) ? $(window) : $(element) ++ this.options = $.extend({}, ScrollSpy.DEFAULTS, options) ++ this.selector = (this.options.target || '') + ' .nav li > a' ++ this.offsets = [] ++ this.targets = [] ++ this.activeTarget = null ++ this.scrollHeight = 0 ++ ++ this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this)) ++ this.refresh() ++ this.process() ++ } ++ ++ ScrollSpy.VERSION = '3.3.7' ++ ++ ScrollSpy.DEFAULTS = { ++ offset: 10 ++ } ++ ++ ScrollSpy.prototype.getScrollHeight = function () { ++ return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight) ++ } ++ ++ ScrollSpy.prototype.refresh = function () { ++ var that = this ++ var offsetMethod = 'offset' ++ var offsetBase = 0 ++ ++ this.offsets = [] ++ this.targets = [] ++ this.scrollHeight = this.getScrollHeight() ++ ++ if (!$.isWindow(this.$scrollElement[0])) { ++ offsetMethod = 'position' ++ offsetBase = this.$scrollElement.scrollTop() ++ } ++ ++ this.$body ++ .find(this.selector) ++ .map(function () { ++ var $el = $(this) ++ var href = $el.data('target') || $el.attr('href') ++ var $href = /^#./.test(href) && $(href) ++ ++ return ($href ++ && $href.length ++ && $href.is(':visible') ++ && [[$href[offsetMethod]().top + offsetBase, href]]) || null ++ }) ++ .sort(function (a, b) { return a[0] - b[0] }) ++ .each(function () { ++ that.offsets.push(this[0]) ++ that.targets.push(this[1]) ++ }) ++ } ++ ++ ScrollSpy.prototype.process = function () { ++ var scrollTop = this.$scrollElement.scrollTop() + this.options.offset ++ var scrollHeight = this.getScrollHeight() ++ var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height() ++ var offsets = this.offsets ++ var targets = this.targets ++ var activeTarget = this.activeTarget ++ var i ++ ++ if (this.scrollHeight != scrollHeight) { ++ this.refresh() ++ } ++ ++ if (scrollTop >= maxScroll) { ++ return activeTarget != (i = targets[targets.length - 1]) && this.activate(i) ++ } ++ ++ if (activeTarget && scrollTop < offsets[0]) { ++ this.activeTarget = null ++ return this.clear() ++ } ++ ++ for (i = offsets.length; i--;) { ++ activeTarget != targets[i] ++ && scrollTop >= offsets[i] ++ && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1]) ++ && this.activate(targets[i]) ++ } ++ } ++ ++ ScrollSpy.prototype.activate = function (target) { ++ this.activeTarget = target ++ ++ this.clear() ++ ++ var selector = this.selector + ++ '[data-target="' + target + '"],' + ++ this.selector + '[href="' + target + '"]' ++ ++ var active = $(selector) ++ .parents('li') ++ .addClass('active') ++ ++ if (active.parent('.dropdown-menu').length) { ++ active = active ++ .closest('li.dropdown') ++ .addClass('active') ++ } ++ ++ active.trigger('activate.bs.scrollspy') ++ } ++ ++ ScrollSpy.prototype.clear = function () { ++ $(this.selector) ++ .parentsUntil(this.options.target, '.active') ++ .removeClass('active') ++ } ++ ++ ++ // SCROLLSPY PLUGIN DEFINITION ++ // =========================== ++ ++ function Plugin(option) { ++ return this.each(function () { ++ var $this = $(this) ++ var data = $this.data('bs.scrollspy') ++ var options = typeof option == 'object' && option ++ ++ if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options))) ++ if (typeof option == 'string') data[option]() ++ }) ++ } ++ ++ var old = $.fn.scrollspy ++ ++ $.fn.scrollspy = Plugin ++ $.fn.scrollspy.Constructor = ScrollSpy ++ ++ ++ // SCROLLSPY NO CONFLICT ++ // ===================== ++ ++ $.fn.scrollspy.noConflict = function () { ++ $.fn.scrollspy = old ++ return this ++ } ++ ++ ++ // SCROLLSPY DATA-API ++ // ================== ++ ++ $(window).on('load.bs.scrollspy.data-api', function () { ++ $('[data-spy="scroll"]').each(function () { ++ var $spy = $(this) ++ Plugin.call($spy, $spy.data()) ++ }) ++ }) ++ ++}(jQuery); ++ ++/* ======================================================================== ++ * Bootstrap: tab.js v3.3.7 ++ * http://getbootstrap.com/javascript/#tabs ++ * ======================================================================== ++ * Copyright 2011-2016 Twitter, Inc. ++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) ++ * ======================================================================== */ ++ ++ +++function ($) { ++ 'use strict'; ++ ++ // TAB CLASS DEFINITION ++ // ==================== ++ ++ var Tab = function (element) { ++ // jscs:disable requireDollarBeforejQueryAssignment ++ this.element = $(element) ++ // jscs:enable requireDollarBeforejQueryAssignment ++ } ++ ++ Tab.VERSION = '3.3.7' ++ ++ Tab.TRANSITION_DURATION = 150 ++ ++ Tab.prototype.show = function () { ++ var $this = this.element ++ var $ul = $this.closest('ul:not(.dropdown-menu)') ++ var selector = $this.data('target') ++ ++ if (!selector) { ++ selector = $this.attr('href') ++ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 ++ } ++ ++ if ($this.parent('li').hasClass('active')) return ++ ++ var $previous = $ul.find('.active:last a') ++ var hideEvent = $.Event('hide.bs.tab', { ++ relatedTarget: $this[0] ++ }) ++ var showEvent = $.Event('show.bs.tab', { ++ relatedTarget: $previous[0] ++ }) ++ ++ $previous.trigger(hideEvent) ++ $this.trigger(showEvent) ++ ++ if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return ++ ++ var $target = $(selector) ++ ++ this.activate($this.closest('li'), $ul) ++ this.activate($target, $target.parent(), function () { ++ $previous.trigger({ ++ type: 'hidden.bs.tab', ++ relatedTarget: $this[0] ++ }) ++ $this.trigger({ ++ type: 'shown.bs.tab', ++ relatedTarget: $previous[0] ++ }) ++ }) ++ } ++ ++ Tab.prototype.activate = function (element, container, callback) { ++ var $active = container.find('> .active') ++ var transition = callback ++ && $.support.transition ++ && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length) ++ ++ function next() { ++ $active ++ .removeClass('active') ++ .find('> .dropdown-menu > .active') ++ .removeClass('active') ++ .end() ++ .find('[data-toggle="tab"]') ++ .attr('aria-expanded', false) ++ ++ element ++ .addClass('active') ++ .find('[data-toggle="tab"]') ++ .attr('aria-expanded', true) ++ ++ if (transition) { ++ element[0].offsetWidth // reflow for transition ++ element.addClass('in') ++ } else { ++ element.removeClass('fade') ++ } ++ ++ if (element.parent('.dropdown-menu').length) { ++ element ++ .closest('li.dropdown') ++ .addClass('active') ++ .end() ++ .find('[data-toggle="tab"]') ++ .attr('aria-expanded', true) ++ } ++ ++ callback && callback() ++ } ++ ++ $active.length && transition ? ++ $active ++ .one('bsTransitionEnd', next) ++ .emulateTransitionEnd(Tab.TRANSITION_DURATION) : ++ next() ++ ++ $active.removeClass('in') ++ } ++ ++ ++ // TAB PLUGIN DEFINITION ++ // ===================== ++ ++ function Plugin(option) { ++ return this.each(function () { ++ var $this = $(this) ++ var data = $this.data('bs.tab') ++ ++ if (!data) $this.data('bs.tab', (data = new Tab(this))) ++ if (typeof option == 'string') data[option]() ++ }) ++ } ++ ++ var old = $.fn.tab ++ ++ $.fn.tab = Plugin ++ $.fn.tab.Constructor = Tab ++ ++ ++ // TAB NO CONFLICT ++ // =============== ++ ++ $.fn.tab.noConflict = function () { ++ $.fn.tab = old ++ return this ++ } ++ ++ ++ // TAB DATA-API ++ // ============ ++ ++ var clickHandler = function (e) { ++ e.preventDefault() ++ Plugin.call($(this), 'show') ++ } ++ ++ $(document) ++ .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler) ++ .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler) ++ ++}(jQuery); ++ ++/* ======================================================================== ++ * Bootstrap: affix.js v3.3.7 ++ * http://getbootstrap.com/javascript/#affix ++ * ======================================================================== ++ * Copyright 2011-2016 Twitter, Inc. ++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) ++ * ======================================================================== */ ++ ++ +++function ($) { ++ 'use strict'; ++ ++ // AFFIX CLASS DEFINITION ++ // ====================== ++ ++ var Affix = function (element, options) { ++ this.options = $.extend({}, Affix.DEFAULTS, options) ++ ++ this.$target = $(this.options.target) ++ .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this)) ++ .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this)) ++ ++ this.$element = $(element) ++ this.affixed = null ++ this.unpin = null ++ this.pinnedOffset = null ++ ++ this.checkPosition() ++ } ++ ++ Affix.VERSION = '3.3.7' ++ ++ Affix.RESET = 'affix affix-top affix-bottom' ++ ++ Affix.DEFAULTS = { ++ offset: 0, ++ target: window ++ } ++ ++ Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) { ++ var scrollTop = this.$target.scrollTop() ++ var position = this.$element.offset() ++ var targetHeight = this.$target.height() ++ ++ if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false ++ ++ if (this.affixed == 'bottom') { ++ if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom' ++ return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom' ++ } ++ ++ var initializing = this.affixed == null ++ var colliderTop = initializing ? scrollTop : position.top ++ var colliderHeight = initializing ? targetHeight : height ++ ++ if (offsetTop != null && scrollTop <= offsetTop) return 'top' ++ if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom' ++ ++ return false ++ } ++ ++ Affix.prototype.getPinnedOffset = function () { ++ if (this.pinnedOffset) return this.pinnedOffset ++ this.$element.removeClass(Affix.RESET).addClass('affix') ++ var scrollTop = this.$target.scrollTop() ++ var position = this.$element.offset() ++ return (this.pinnedOffset = position.top - scrollTop) ++ } ++ ++ Affix.prototype.checkPositionWithEventLoop = function () { ++ setTimeout($.proxy(this.checkPosition, this), 1) ++ } ++ ++ Affix.prototype.checkPosition = function () { ++ if (!this.$element.is(':visible')) return ++ ++ var height = this.$element.height() ++ var offset = this.options.offset ++ var offsetTop = offset.top ++ var offsetBottom = offset.bottom ++ var scrollHeight = Math.max($(document).height(), $(document.body).height()) ++ ++ if (typeof offset != 'object') offsetBottom = offsetTop = offset ++ if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element) ++ if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element) ++ ++ var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom) ++ ++ if (this.affixed != affix) { ++ if (this.unpin != null) this.$element.css('top', '') ++ ++ var affixType = 'affix' + (affix ? '-' + affix : '') ++ var e = $.Event(affixType + '.bs.affix') ++ ++ this.$element.trigger(e) ++ ++ if (e.isDefaultPrevented()) return ++ ++ this.affixed = affix ++ this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null ++ ++ this.$element ++ .removeClass(Affix.RESET) ++ .addClass(affixType) ++ .trigger(affixType.replace('affix', 'affixed') + '.bs.affix') ++ } ++ ++ if (affix == 'bottom') { ++ this.$element.offset({ ++ top: scrollHeight - height - offsetBottom ++ }) ++ } ++ } ++ ++ ++ // AFFIX PLUGIN DEFINITION ++ // ======================= ++ ++ function Plugin(option) { ++ return this.each(function () { ++ var $this = $(this) ++ var data = $this.data('bs.affix') ++ var options = typeof option == 'object' && option ++ ++ if (!data) $this.data('bs.affix', (data = new Affix(this, options))) ++ if (typeof option == 'string') data[option]() ++ }) ++ } ++ ++ var old = $.fn.affix ++ ++ $.fn.affix = Plugin ++ $.fn.affix.Constructor = Affix ++ ++ ++ // AFFIX NO CONFLICT ++ // ================= ++ ++ $.fn.affix.noConflict = function () { ++ $.fn.affix = old ++ return this ++ } ++ ++ ++ // AFFIX DATA-API ++ // ============== ++ ++ $(window).on('load', function () { ++ $('[data-spy="affix"]').each(function () { ++ var $spy = $(this) ++ var data = $spy.data() ++ ++ data.offset = data.offset || {} ++ ++ if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom ++ if (data.offsetTop != null) data.offset.top = data.offsetTop ++ ++ Plugin.call($spy, data) ++ }) ++ }) ++ ++}(jQuery); diff --cc debian/missing-sources/two.js index 000000000,000000000..859d3b418 new file mode 100644 --- /dev/null +++ b/debian/missing-sources/two.js @@@ -1,0 -1,0 +1,10244 @@@ ++/** ++ * two.js ++ * a two-dimensional drawing api meant for modern browsers. It is renderer ++ * agnostic enabling the same api for rendering in multiple contexts: webgl, ++ * canvas2d, and svg. ++ * ++ * Copyright (c) 2012 - 2017 jonobr1 / http://jonobr1.com ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ * ++ */ ++ ++this.Two = (function(previousTwo) { ++ ++ var root = typeof window != 'undefined' ? window : typeof global != 'undefined' ? global : null; ++ var toString = Object.prototype.toString; ++ var _ = { ++ // http://underscorejs.org/ • 1.8.3 ++ _indexAmount: 0, ++ natural: { ++ slice: Array.prototype.slice, ++ indexOf: Array.prototype.indexOf, ++ keys: Object.keys, ++ bind: Function.prototype.bind, ++ create: Object.create ++ }, ++ identity: function(value) { ++ return value; ++ }, ++ isArguments: function(obj) { ++ return toString.call(obj) === '[object Arguments]'; ++ }, ++ isFunction: function(obj) { ++ return toString.call(obj) === '[object Function]'; ++ }, ++ isString: function(obj) { ++ return toString.call(obj) === '[object String]'; ++ }, ++ isNumber: function(obj) { ++ return toString.call(obj) === '[object Number]'; ++ }, ++ isDate: function(obj) { ++ return toString.call(obj) === '[object Date]'; ++ }, ++ isRegExp: function(obj) { ++ return toString.call(obj) === '[object RegExp]'; ++ }, ++ isError: function(obj) { ++ return toString.call(obj) === '[object Error]'; ++ }, ++ isFinite: function(obj) { ++ return isFinite(obj) && !isNaN(parseFloat(obj)); ++ }, ++ isNaN: function(obj) { ++ return _.isNumber(obj) && obj !== +obj; ++ }, ++ isBoolean: function(obj) { ++ return obj === true || obj === false || toString.call(obj) === '[object Boolean]'; ++ }, ++ isNull: function(obj) { ++ return obj === null; ++ }, ++ isUndefined: function(obj) { ++ return obj === void 0; ++ }, ++ isEmpty: function(obj) { ++ if (obj == null) return true; ++ if (isArrayLike && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0; ++ return _.keys(obj).length === 0; ++ }, ++ isElement: function(obj) { ++ return !!(obj && obj.nodeType === 1); ++ }, ++ isArray: Array.isArray || function(obj) { ++ return toString.call(obj) === '[object Array]'; ++ }, ++ isObject: function(obj) { ++ var type = typeof obj; ++ return type === 'function' || type === 'object' && !!obj; ++ }, ++ toArray: function(obj) { ++ if (!obj) { ++ return []; ++ } ++ if (_.isArray(obj)) { ++ return slice.call(obj); ++ } ++ if (isArrayLike(obj)) { ++ return _.map(obj, _.identity); ++ } ++ return _.values(obj); ++ }, ++ range: function(start, stop, step) { ++ if (stop == null) { ++ stop = start || 0; ++ start = 0; ++ } ++ step = step || 1; ++ ++ var length = Math.max(Math.ceil((stop - start) / step), 0); ++ var range = Array(length); ++ ++ for (var idx = 0; idx < length; idx++, start += step) { ++ range[idx] = start; ++ } ++ ++ return range; ++ }, ++ indexOf: function(list, item) { ++ if (!!_.natural.indexOf) { ++ return _.natural.indexOf.call(list, item); ++ } ++ for (var i = 0; i < list.length; i++) { ++ if (list[i] === item) { ++ return i; ++ } ++ } ++ return -1; ++ }, ++ has: function(obj, key) { ++ return obj != null && hasOwnProperty.call(obj, key); ++ }, ++ bind: function(func, ctx) { ++ var natural = _.natural.bind; ++ if (natural && func.bind === natural) { ++ return natural.apply(func, slice.call(arguments, 1)); ++ } ++ var args = slice.call(arguments, 2); ++ return function() { ++ func.apply(ctx, args); ++ }; ++ }, ++ extend: function(base) { ++ var sources = slice.call(arguments, 1); ++ for (var i = 0; i < sources.length; i++) { ++ var obj = sources[i]; ++ for (var k in obj) { ++ base[k] = obj[k]; ++ } ++ } ++ return base; ++ }, ++ defaults: function(base) { ++ var sources = slice.call(arguments, 1); ++ for (var i = 0; i < sources.length; i++) { ++ var obj = sources[i]; ++ for (var k in obj) { ++ if (base[k] === void 0) { ++ base[k] = obj[k]; ++ } ++ } ++ } ++ return base; ++ }, ++ keys: function(obj) { ++ if (!_.isObject(obj)) { ++ return []; ++ } ++ if (_.natural.keys) { ++ return _.natural.keys(obj); ++ } ++ var keys = []; ++ for (var k in obj) { ++ if (_.has(obj, k)) { ++ keys.push(k); ++ } ++ } ++ return keys; ++ }, ++ values: function(obj) { ++ var keys = _.keys(obj); ++ var values = []; ++ for (var i = 0; i < keys.length; i++) { ++ var k = keys[i]; ++ values.push(obj[k]); ++ } ++ return values; ++ }, ++ each: function(obj, iteratee, context) { ++ var ctx = context || this; ++ var keys = !isArrayLike(obj) && _.keys(obj); ++ var length = (keys || obj).length; ++ for (var i = 0; i < length; i++) { ++ var k = keys ? keys[i] : i; ++ iteratee.call(ctx, obj[k], k, obj); ++ } ++ return obj; ++ }, ++ map: function(obj, iteratee, context) { ++ var ctx = context || this; ++ var keys = !isArrayLike(obj) && _.keys(obj); ++ var length = (keys || obj).length; ++ var result = []; ++ for (var i = 0; i < length; i++) { ++ var k = keys ? keys[i] : i; ++ result[i] = iteratee.call(ctx, obj[k], k, obj); ++ } ++ return result; ++ }, ++ once: function(func) { ++ var init = false; ++ return function() { ++ if (!!init) { ++ return func; ++ } ++ init = true; ++ return func.apply(this, arguments); ++ } ++ }, ++ after: function(times, func) { ++ return function() { ++ while (--times < 1) { ++ return func.apply(this, arguments); ++ } ++ } ++ }, ++ uniqueId: function(prefix) { ++ var id = ++_._indexAmount + ''; ++ return prefix ? prefix + id : id; ++ } ++ }; ++ ++ /** ++ * Constants ++ */ ++ ++ var sin = Math.sin, ++ cos = Math.cos, ++ atan2 = Math.atan2, ++ sqrt = Math.sqrt, ++ round = Math.round, ++ abs = Math.abs, ++ PI = Math.PI, ++ TWO_PI = PI * 2, ++ HALF_PI = PI / 2, ++ pow = Math.pow, ++ min = Math.min, ++ max = Math.max; ++ ++ /** ++ * Localized variables ++ */ ++ ++ var count = 0; ++ var slice = _.natural.slice; ++ var perf = ((root.performance && root.performance.now) ? root.performance : Date); ++ var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; ++ var getLength = function(obj) { ++ return obj == null ? void 0 : obj['length']; ++ }; ++ var isArrayLike = function(collection) { ++ var length = getLength(collection); ++ return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX; ++ }; ++ ++ /** ++ * Cross browser dom events. ++ */ ++ var dom = { ++ ++ temp: (root.document ? root.document.createElement('div') : {}), ++ ++ hasEventListeners: _.isFunction(root.addEventListener), ++ ++ bind: function(elem, event, func, bool) { ++ if (this.hasEventListeners) { ++ elem.addEventListener(event, func, !!bool); ++ } else { ++ elem.attachEvent('on' + event, func); ++ } ++ return dom; ++ }, ++ ++ unbind: function(elem, event, func, bool) { ++ if (dom.hasEventListeners) { ++ elem.removeEventListeners(event, func, !!bool); ++ } else { ++ elem.detachEvent('on' + event, func); ++ } ++ return dom; ++ }, ++ ++ getRequestAnimationFrame: function() { ++ ++ var lastTime = 0; ++ var vendors = ['ms', 'moz', 'webkit', 'o']; ++ var request = root.requestAnimationFrame, cancel; ++ ++ if(!request) { ++ for (var i = 0; i < vendors.length; i++) { ++ request = root[vendors[i] + 'RequestAnimationFrame'] || request; ++ cancel = root[vendors[i] + 'CancelAnimationFrame'] ++ || root[vendors[i] + 'CancelRequestAnimationFrame'] || cancel; ++ } ++ ++ request = request || function(callback, element) { ++ var currTime = new Date().getTime(); ++ var timeToCall = Math.max(0, 16 - (currTime - lastTime)); ++ var id = root.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall); ++ lastTime = currTime + timeToCall; ++ return id; ++ }; ++ // cancel = cancel || function(id) { ++ // clearTimeout(id); ++ // }; ++ } ++ ++ request.init = _.once(loop); ++ ++ return request; ++ ++ } ++ ++ }; ++ ++ /** ++ * @class ++ */ ++ var Two = root.Two = function(options) { ++ ++ // Determine what Renderer to use and setup a scene. ++ ++ var params = _.defaults(options || {}, { ++ fullscreen: false, ++ width: 640, ++ height: 480, ++ type: Two.Types.svg, ++ autostart: false ++ }); ++ ++ _.each(params, function(v, k) { ++ if (k === 'fullscreen' || k === 'autostart') { ++ return; ++ } ++ this[k] = v; ++ }, this); ++ ++ // Specified domElement overrides type declaration only if the element does not support declared renderer type. ++ if (_.isElement(params.domElement)) { ++ var tagName = params.domElement.tagName.toLowerCase(); ++ // TODO: Reconsider this if statement's logic. ++ if (!/^(CanvasRenderer-canvas|WebGLRenderer-canvas|SVGRenderer-svg)$/.test(this.type+'-'+tagName)) { ++ this.type = Two.Types[tagName]; ++ } ++ } ++ ++ this.renderer = new Two[this.type](this); ++ Two.Utils.setPlaying.call(this, params.autostart); ++ this.frameCount = 0; ++ ++ if (params.fullscreen) { ++ ++ var fitted = _.bind(fitToWindow, this); ++ _.extend(document.body.style, { ++ overflow: 'hidden', ++ margin: 0, ++ padding: 0, ++ top: 0, ++ left: 0, ++ right: 0, ++ bottom: 0, ++ position: 'fixed' ++ }); ++ _.extend(this.renderer.domElement.style, { ++ display: 'block', ++ top: 0, ++ left: 0, ++ right: 0, ++ bottom: 0, ++ position: 'fixed' ++ }); ++ dom.bind(root, 'resize', fitted); ++ fitted(); ++ ++ ++ } else if (!_.isElement(params.domElement)) { ++ ++ this.renderer.setSize(params.width, params.height, this.ratio); ++ this.width = params.width; ++ this.height = params.height; ++ ++ } ++ ++ this.scene = this.renderer.scene; ++ ++ Two.Instances.push(this); ++ raf.init(); ++ ++ }; ++ ++ _.extend(Two, { ++ ++ /** ++ * Access to root in other files. ++ */ ++ ++ root: root, ++ ++ /** ++ * Primitive ++ */ ++ ++ Array: root.Float32Array || Array, ++ ++ Types: { ++ webgl: 'WebGLRenderer', ++ svg: 'SVGRenderer', ++ canvas: 'CanvasRenderer' ++ }, ++ ++ Version: 'v0.7.0', ++ ++ Identifier: 'two_', ++ ++ Properties: { ++ hierarchy: 'hierarchy', ++ demotion: 'demotion' ++ }, ++ ++ Events: { ++ play: 'play', ++ pause: 'pause', ++ update: 'update', ++ render: 'render', ++ resize: 'resize', ++ change: 'change', ++ remove: 'remove', ++ insert: 'insert', ++ order: 'order', ++ load: 'load' ++ }, ++ ++ Commands: { ++ move: 'M', ++ line: 'L', ++ curve: 'C', ++ close: 'Z' ++ }, ++ ++ Resolution: 8, ++ ++ Instances: [], ++ ++ noConflict: function() { ++ root.Two = previousTwo; ++ return this; ++ }, ++ ++ uniqueId: function() { ++ var id = count; ++ count++; ++ return id; ++ }, ++ ++ Utils: _.extend(_, { ++ ++ performance: perf, ++ ++ defineProperty: function(property) { ++ ++ var object = this; ++ var secret = '_' + property; ++ var flag = '_flag' + property.charAt(0).toUpperCase() + property.slice(1); ++ ++ Object.defineProperty(object, property, { ++ enumerable: true, ++ get: function() { ++ return this[secret]; ++ }, ++ set: function(v) { ++ this[secret] = v; ++ this[flag] = true; ++ } ++ }); ++ ++ }, ++ ++ /** ++ * Release an arbitrary class' events from the two.js corpus and recurse ++ * through its children and or vertices. ++ */ ++ release: function(obj) { ++ ++ if (!_.isObject(obj)) { ++ return; ++ } ++ ++ if (_.isFunction(obj.unbind)) { ++ obj.unbind(); ++ } ++ ++ if (obj.vertices) { ++ if (_.isFunction(obj.vertices.unbind)) { ++ obj.vertices.unbind(); ++ } ++ _.each(obj.vertices, function(v) { ++ if (_.isFunction(v.unbind)) { ++ v.unbind(); ++ } ++ }); ++ } ++ ++ if (obj.children) { ++ _.each(obj.children, function(obj) { ++ Two.Utils.release(obj); ++ }); ++ } ++ ++ }, ++ ++ xhr: function(path, callback) { ++ ++ var xhr = new XMLHttpRequest(); ++ xhr.open('GET', path); ++ ++ xhr.onreadystatechange = function() { ++ if (xhr.readyState === 4 && xhr.status === 200) { ++ callback(xhr.responseText); ++ } ++ }; ++ ++ xhr.send(); ++ return xhr; ++ ++ }, ++ ++ Curve: { ++ ++ CollinearityEpsilon: pow(10, -30), ++ ++ RecursionLimit: 16, ++ ++ CuspLimit: 0, ++ ++ Tolerance: { ++ distance: 0.25, ++ angle: 0, ++ epsilon: 0.01 ++ }, ++ ++ // Lookup tables for abscissas and weights with values for n = 2 .. 16. ++ // As values are symmetric, only store half of them and adapt algorithm ++ // to factor in symmetry. ++ abscissas: [ ++ [ 0.5773502691896257645091488], ++ [0,0.7745966692414833770358531], ++ [ 0.3399810435848562648026658,0.8611363115940525752239465], ++ [0,0.5384693101056830910363144,0.9061798459386639927976269], ++ [ 0.2386191860831969086305017,0.6612093864662645136613996,0.9324695142031520278123016], ++ [0,0.4058451513773971669066064,0.7415311855993944398638648,0.9491079123427585245261897], ++ [ 0.1834346424956498049394761,0.5255324099163289858177390,0.7966664774136267395915539,0.9602898564975362316835609], ++ [0,0.3242534234038089290385380,0.6133714327005903973087020,0.8360311073266357942994298,0.9681602395076260898355762], ++ [ 0.1488743389816312108848260,0.4333953941292471907992659,0.6794095682990244062343274,0.8650633666889845107320967,0.9739065285171717200779640], ++ [0,0.2695431559523449723315320,0.5190961292068118159257257,0.7301520055740493240934163,0.8870625997680952990751578,0.9782286581460569928039380], ++ [ 0.1252334085114689154724414,0.3678314989981801937526915,0.5873179542866174472967024,0.7699026741943046870368938,0.9041172563704748566784659,0.9815606342467192506905491], ++ [0,0.2304583159551347940655281,0.4484927510364468528779129,0.6423493394403402206439846,0.8015780907333099127942065,0.9175983992229779652065478,0.9841830547185881494728294], ++ [ 0.1080549487073436620662447,0.3191123689278897604356718,0.5152486363581540919652907,0.6872929048116854701480198,0.8272013150697649931897947,0.9284348836635735173363911,0.9862838086968123388415973], ++ [0,0.2011940939974345223006283,0.3941513470775633698972074,0.5709721726085388475372267,0.7244177313601700474161861,0.8482065834104272162006483,0.9372733924007059043077589,0.9879925180204854284895657], ++ [ 0.0950125098376374401853193,0.2816035507792589132304605,0.4580167776572273863424194,0.6178762444026437484466718,0.7554044083550030338951012,0.8656312023878317438804679,0.9445750230732325760779884,0.9894009349916499325961542] ++ ], ++ ++ weights: [ ++ [1], ++ [0.8888888888888888888888889,0.5555555555555555555555556], ++ [0.6521451548625461426269361,0.3478548451374538573730639], ++ [0.5688888888888888888888889,0.4786286704993664680412915,0.2369268850561890875142640], ++ [0.4679139345726910473898703,0.3607615730481386075698335,0.1713244923791703450402961], ++ [0.4179591836734693877551020,0.3818300505051189449503698,0.2797053914892766679014678,0.1294849661688696932706114], ++ [0.3626837833783619829651504,0.3137066458778872873379622,0.2223810344533744705443560,0.1012285362903762591525314], ++ [0.3302393550012597631645251,0.3123470770400028400686304,0.2606106964029354623187429,0.1806481606948574040584720,0.0812743883615744119718922], ++ [0.2955242247147528701738930,0.2692667193099963550912269,0.2190863625159820439955349,0.1494513491505805931457763,0.0666713443086881375935688], ++ [0.2729250867779006307144835,0.2628045445102466621806889,0.2331937645919904799185237,0.1862902109277342514260976,0.1255803694649046246346943,0.0556685671161736664827537], ++ [0.2491470458134027850005624,0.2334925365383548087608499,0.2031674267230659217490645,0.1600783285433462263346525,0.1069393259953184309602547,0.0471753363865118271946160], ++ [0.2325515532308739101945895,0.2262831802628972384120902,0.2078160475368885023125232,0.1781459807619457382800467,0.1388735102197872384636018,0.0921214998377284479144218,0.0404840047653158795200216], ++ [0.2152638534631577901958764,0.2051984637212956039659241,0.1855383974779378137417166,0.1572031671581935345696019,0.1215185706879031846894148,0.0801580871597602098056333,0.0351194603317518630318329], ++ [0.2025782419255612728806202,0.1984314853271115764561183,0.1861610000155622110268006,0.1662692058169939335532009,0.1395706779261543144478048,0.1071592204671719350118695,0.0703660474881081247092674,0.0307532419961172683546284], ++ [0.1894506104550684962853967,0.1826034150449235888667637,0.1691565193950025381893121,0.1495959888165767320815017,0.1246289712555338720524763,0.0951585116824927848099251,0.0622535239386478928628438,0.0271524594117540948517806] ++ ] ++ ++ }, ++ ++ /** ++ * Account for high dpi rendering. ++ * http://www.html5rocks.com/en/tutorials/canvas/hidpi/ ++ */ ++ ++ devicePixelRatio: root.devicePixelRatio || 1, ++ ++ getBackingStoreRatio: function(ctx) { ++ return ctx.webkitBackingStorePixelRatio || ++ ctx.mozBackingStorePixelRatio || ++ ctx.msBackingStorePixelRatio || ++ ctx.oBackingStorePixelRatio || ++ ctx.backingStorePixelRatio || 1; ++ }, ++ ++ getRatio: function(ctx) { ++ return Two.Utils.devicePixelRatio / getBackingStoreRatio(ctx); ++ }, ++ ++ /** ++ * Properly defer play calling until after all objects ++ * have been updated with their newest styles. ++ */ ++ setPlaying: function(b) { ++ ++ this.playing = !!b; ++ return this; ++ ++ }, ++ ++ /** ++ * Return the computed matrix of a nested object. ++ * TODO: Optimize traversal. ++ */ ++ getComputedMatrix: function(object, matrix) { ++ ++ matrix = (matrix && matrix.identity()) || new Two.Matrix(); ++ var parent = object, matrices = []; ++ ++ while (parent && parent._matrix) { ++ matrices.push(parent._matrix); ++ parent = parent.parent; ++ } ++ ++ matrices.reverse(); ++ ++ _.each(matrices, function(m) { ++ ++ var e = m.elements; ++ matrix.multiply( ++ e[0], e[1], e[2], e[3], e[4], e[5], e[6], e[7], e[8], e[9]); ++ ++ }); ++ ++ return matrix; ++ ++ }, ++ ++ deltaTransformPoint: function(matrix, x, y) { ++ ++ var dx = x * matrix.a + y * matrix.c + 0; ++ var dy = x * matrix.b + y * matrix.d + 0; ++ ++ return new Two.Vector(dx, dy); ++ ++ }, ++ ++ /** ++ * https://gist.github.com/2052247 ++ */ ++ decomposeMatrix: function(matrix) { ++ ++ // calculate delta transform point ++ var px = Two.Utils.deltaTransformPoint(matrix, 0, 1); ++ var py = Two.Utils.deltaTransformPoint(matrix, 1, 0); ++ ++ // calculate skew ++ var skewX = ((180 / Math.PI) * Math.atan2(px.y, px.x) - 90); ++ var skewY = ((180 / Math.PI) * Math.atan2(py.y, py.x)); ++ ++ return { ++ translateX: matrix.e, ++ translateY: matrix.f, ++ scaleX: Math.sqrt(matrix.a * matrix.a + matrix.b * matrix.b), ++ scaleY: Math.sqrt(matrix.c * matrix.c + matrix.d * matrix.d), ++ skewX: skewX, ++ skewY: skewY, ++ rotation: skewX // rotation is the same as skew x ++ }; ++ ++ }, ++ ++ /** ++ * Walk through item properties and pick the ones of interest. ++ * Will try to resolve styles applied via CSS ++ * ++ * TODO: Reverse calculate `Two.Gradient`s for fill / stroke ++ * of any given path. ++ */ ++ applySvgAttributes: function(node, elem) { ++ ++ var attributes = {}, styles = {}, i, key, value, attr; ++ ++ // Not available in non browser environments ++ if (getComputedStyle) { ++ // Convert CSSStyleDeclaration to a normal object ++ var computedStyles = getComputedStyle(node); ++ i = computedStyles.length; ++ ++ while (i--) { ++ key = computedStyles[i]; ++ value = computedStyles[key]; ++ // Gecko returns undefined for unset properties ++ // Webkit returns the default value ++ if (value !== undefined) { ++ styles[key] = value; ++ } ++ } ++ } ++ ++ // Convert NodeMap to a normal object ++ i = node.attributes.length; ++ while (i--) { ++ attr = node.attributes[i]; ++ attributes[attr.nodeName] = attr.value; ++ } ++ ++ // Getting the correct opacity is a bit tricky, since SVG path elements don't ++ // support opacity as an attribute, but you can apply it via CSS. ++ // So we take the opacity and set (stroke/fill)-opacity to the same value. ++ if (!_.isUndefined(styles.opacity)) { ++ styles['stroke-opacity'] = styles.opacity; ++ styles['fill-opacity'] = styles.opacity; ++ } ++ ++ // Merge attributes and applied styles (attributes take precedence) ++ _.extend(styles, attributes); ++ ++ // Similarly visibility is influenced by the value of both display and visibility. ++ // Calculate a unified value here which defaults to `true`. ++ styles.visible = !(_.isUndefined(styles.display) && styles.display === 'none') ++ || (_.isUndefined(styles.visibility) && styles.visibility === 'hidden'); ++ ++ // Now iterate the whole thing ++ for (key in styles) { ++ value = styles[key]; ++ ++ switch (key) { ++ case 'transform': ++ // TODO: Check this out https://github.com/paperjs/paper.js/blob/master/src/svg/SVGImport.js#L313 ++ if (value === 'none') break; ++ var m = node.getCTM ? node.getCTM() : null; ++ ++ // Might happen when transform string is empty or not valid. ++ if (m === null) break; ++ ++ // // Option 1: edit the underlying matrix and don't force an auto calc. ++ // var m = node.getCTM(); ++ // elem._matrix.manual = true; ++ // elem._matrix.set(m.a, m.b, m.c, m.d, m.e, m.f); ++ ++ // Option 2: Decompose and infer Two.js related properties. ++ var transforms = Two.Utils.decomposeMatrix(node.getCTM()); ++ ++ elem.translation.set(transforms.translateX, transforms.translateY); ++ elem.rotation = transforms.rotation; ++ // Warning: Two.js elements only support uniform scalars... ++ elem.scale = transforms.scaleX; ++ ++ var x = parseFloat((styles.x + '').replace('px')); ++ var y = parseFloat((styles.y + '').replace('px')); ++ ++ // Override based on attributes. ++ if (x) { ++ elem.translation.x = x; ++ } ++ ++ if (y) { ++ elem.translation.y = y; ++ } ++ ++ break; ++ case 'visible': ++ elem.visible = value; ++ break; ++ case 'stroke-linecap': ++ elem.cap = value; ++ break; ++ case 'stroke-linejoin': ++ elem.join = value; ++ break; ++ case 'stroke-miterlimit': ++ elem.miter = value; ++ break; ++ case 'stroke-width': ++ elem.linewidth = parseFloat(value); ++ break; ++ case 'stroke-opacity': ++ case 'fill-opacity': ++ case 'opacity': ++ elem.opacity = parseFloat(value); ++ break; ++ case 'fill': ++ case 'stroke': ++ if (/url\(\#.*\)/i.test(value)) { ++ elem[key] = this.getById( ++ value.replace(/url\(\#(.*)\)/i, '$1')); ++ } else { ++ elem[key] = (value === 'none') ? 'transparent' : value; ++ } ++ break; ++ case 'id': ++ elem.id = value; ++ break; ++ case 'class': ++ elem.classList = value.split(' '); ++ break; ++ } ++ } ++ ++ return elem; ++ ++ }, ++ ++ /** ++ * Read any number of SVG node types and create Two equivalents of them. ++ */ ++ read: { ++ ++ svg: function() { ++ return Two.Utils.read.g.apply(this, arguments); ++ }, ++ ++ g: function(node) { ++ ++ var group = new Two.Group(); ++ ++ // Switched up order to inherit more specific styles ++ Two.Utils.applySvgAttributes.call(this, node, group); ++ ++ for (var i = 0, l = node.childNodes.length; i < l; i++) { ++ var n = node.childNodes[i]; ++ var tag = n.nodeName; ++ if (!tag) return; ++ ++ var tagName = tag.replace(/svg\:/ig, '').toLowerCase(); ++ ++ if (tagName in Two.Utils.read) { ++ var o = Two.Utils.read[tagName].call(group, n); ++ group.add(o); ++ } ++ } ++ ++ return group; ++ ++ }, ++ ++ polygon: function(node, open) { ++ ++ var points = node.getAttribute('points'); ++ ++ var verts = []; ++ points.replace(/(-?[\d\.?]+)[,|\s](-?[\d\.?]+)/g, function(match, p1, p2) { ++ verts.push(new Two.Anchor(parseFloat(p1), parseFloat(p2))); ++ }); ++ ++ var poly = new Two.Path(verts, !open).noStroke(); ++ poly.fill = 'black'; ++ ++ return Two.Utils.applySvgAttributes.call(this, node, poly); ++ ++ }, ++ ++ polyline: function(node) { ++ return Two.Utils.read.polygon.call(this, node, true); ++ }, ++ ++ path: function(node) { ++ ++ var path = node.getAttribute('d'); ++ ++ // Create a Two.Path from the paths. ++ ++ var coord = new Two.Anchor(); ++ var control, coords; ++ var closed = false, relative = false; ++ var commands = path.match(/[a-df-z][^a-df-z]*/ig); ++ var last = commands.length - 1; ++ ++ // Split up polybeziers ++ ++ _.each(commands.slice(0), function(command, i) { ++ ++ var type = command[0]; ++ var lower = type.toLowerCase(); ++ var items = command.slice(1).trim().split(/[\s,]+|(?=\s?[+\-])/); ++ var pre, post, result = [], bin; ++ ++ if (i <= 0) { ++ commands = []; ++ } ++ ++ switch (lower) { ++ case 'h': ++ case 'v': ++ if (items.length > 1) { ++ bin = 1; ++ } ++ break; ++ case 'm': ++ case 'l': ++ case 't': ++ if (items.length > 2) { ++ bin = 2; ++ } ++ break; ++ case 's': ++ case 'q': ++ if (items.length > 4) { ++ bin = 4; ++ } ++ break; ++ case 'c': ++ if (items.length > 6) { ++ bin = 6; ++ } ++ break; ++ case 'a': ++ // TODO: Handle Ellipses ++ break; ++ } ++ ++ if (bin) { ++ ++ for (var j = 0, l = items.length, times = 0; j < l; j+=bin) { ++ ++ var ct = type; ++ if (times > 0) { ++ ++ switch (type) { ++ case 'm': ++ ct = 'l'; ++ break; ++ case 'M': ++ ct = 'L'; ++ break; ++ } ++ ++ } ++ ++ result.push([ct].concat(items.slice(j, j + bin)).join(' ')); ++ times++; ++ ++ } ++ ++ commands = Array.prototype.concat.apply(commands, result); ++ ++ } else { ++ ++ commands.push(command); ++ ++ } ++ ++ }); ++ ++ // Create the vertices for our Two.Path ++ ++ var points = []; ++ _.each(commands, function(command, i) { ++ ++ var result, x, y; ++ var type = command[0]; ++ var lower = type.toLowerCase(); ++ ++ coords = command.slice(1).trim(); ++ coords = coords.replace(/(-?\d+(?:\.\d*)?)[eE]([+\-]?\d+)/g, function(match, n1, n2) { ++ return parseFloat(n1) * pow(10, n2); ++ }); ++ coords = coords.split(/[\s,]+|(?=\s?[+\-])/); ++ relative = type === lower; ++ ++ var x1, y1, x2, y2, x3, y3, x4, y4, reflection; ++ ++ switch (lower) { ++ ++ case 'z': ++ if (i >= last) { ++ closed = true; ++ } else { ++ x = coord.x; ++ y = coord.y; ++ result = new Two.Anchor( ++ x, y, ++ undefined, undefined, ++ undefined, undefined, ++ Two.Commands.close ++ ); ++ } ++ break; ++ ++ case 'm': ++ case 'l': ++ ++ x = parseFloat(coords[0]); ++ y = parseFloat(coords[1]); ++ ++ result = new Two.Anchor( ++ x, y, ++ undefined, undefined, ++ undefined, undefined, ++ lower === 'm' ? Two.Commands.move : Two.Commands.line ++ ); ++ ++ if (relative) { ++ result.addSelf(coord); ++ } ++ ++ // result.controls.left.copy(result); ++ // result.controls.right.copy(result); ++ ++ coord = result; ++ break; ++ ++ case 'h': ++ case 'v': ++ ++ var a = lower === 'h' ? 'x' : 'y'; ++ var b = a === 'x' ? 'y' : 'x'; ++ ++ result = new Two.Anchor( ++ undefined, undefined, ++ undefined, undefined, ++ undefined, undefined, ++ Two.Commands.line ++ ); ++ result[a] = parseFloat(coords[0]); ++ result[b] = coord[b]; ++ ++ if (relative) { ++ result[a] += coord[a]; ++ } ++ ++ // result.controls.left.copy(result); ++ // result.controls.right.copy(result); ++ ++ coord = result; ++ break; ++ ++ case 'c': ++ case 's': ++ ++ x1 = coord.x; ++ y1 = coord.y; ++ ++ if (!control) { ++ control = new Two.Vector();//.copy(coord); ++ } ++ ++ if (lower === 'c') { ++ ++ x2 = parseFloat(coords[0]); ++ y2 = parseFloat(coords[1]); ++ x3 = parseFloat(coords[2]); ++ y3 = parseFloat(coords[3]); ++ x4 = parseFloat(coords[4]); ++ y4 = parseFloat(coords[5]); ++ ++ } else { ++ ++ // Calculate reflection control point for proper x2, y2 ++ // inclusion. ++ ++ reflection = getReflection(coord, control, relative); ++ ++ x2 = reflection.x; ++ y2 = reflection.y; ++ x3 = parseFloat(coords[0]); ++ y3 = parseFloat(coords[1]); ++ x4 = parseFloat(coords[2]); ++ y4 = parseFloat(coords[3]); ++ ++ } ++ ++ if (relative) { ++ x2 += x1; ++ y2 += y1; ++ x3 += x1; ++ y3 += y1; ++ x4 += x1; ++ y4 += y1; ++ } ++ ++ if (!_.isObject(coord.controls)) { ++ Two.Anchor.AppendCurveProperties(coord); ++ } ++ ++ coord.controls.right.set(x2 - coord.x, y2 - coord.y); ++ result = new Two.Anchor( ++ x4, y4, ++ x3 - x4, y3 - y4, ++ undefined, undefined, ++ Two.Commands.curve ++ ); ++ ++ coord = result; ++ control = result.controls.left; ++ ++ break; ++ ++ case 't': ++ case 'q': ++ ++ x1 = coord.x; ++ y1 = coord.y; ++ ++ if (!control) { ++ control = new Two.Vector();//.copy(coord); ++ } ++ ++ if (control.isZero()) { ++ x2 = x1; ++ y2 = y1; ++ } else { ++ x2 = control.x; ++ y1 = control.y; ++ } ++ ++ if (lower === 'q') { ++ ++ x3 = parseFloat(coords[0]); ++ y3 = parseFloat(coords[1]); ++ x4 = parseFloat(coords[1]); ++ y4 = parseFloat(coords[2]); ++ ++ } else { ++ ++ reflection = getReflection(coord, control, relative); ++ ++ x3 = reflection.x; ++ y3 = reflection.y; ++ x4 = parseFloat(coords[0]); ++ y4 = parseFloat(coords[1]); ++ ++ } ++ ++ if (relative) { ++ x2 += x1; ++ y2 += y1; ++ x3 += x1; ++ y3 += y1; ++ x4 += x1; ++ y4 += y1; ++ } ++ ++ if (!_.isObject(coord.controls)) { ++ Two.Anchor.AppendCurveProperties(coord); ++ } ++ ++ coord.controls.right.set(x2 - coord.x, y2 - coord.y); ++ result = new Two.Anchor( ++ x4, y4, ++ x3 - x4, y3 - y4, ++ undefined, undefined, ++ Two.Commands.curve ++ ); ++ ++ coord = result; ++ control = result.controls.left; ++ ++ break; ++ ++ case 'a': ++ ++ // throw new Two.Utils.Error('not yet able to interpret Elliptical Arcs.'); ++ x1 = coord.x; ++ y1 = coord.y; ++ ++ var rx = parseFloat(coords[0]); ++ var ry = parseFloat(coords[1]); ++ var xAxisRotation = parseFloat(coords[2]) * Math.PI / 180; ++ var largeArcFlag = parseFloat(coords[3]); ++ var sweepFlag = parseFloat(coords[4]); ++ ++ x4 = parseFloat(coords[5]); ++ y4 = parseFloat(coords[6]); ++ ++ if (relative) { ++ x4 += x1; ++ y4 += y1; ++ } ++ ++ // http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter ++ ++ // Calculate midpoint mx my ++ var mx = (x4 - x1) / 2; ++ var my = (y4 - y1) / 2; ++ ++ // Calculate x1' y1' F.6.5.1 ++ var _x = mx * Math.cos(xAxisRotation) + my * Math.sin(xAxisRotation); ++ var _y = - mx * Math.sin(xAxisRotation) + my * Math.cos(xAxisRotation); ++ ++ var rx2 = rx * rx; ++ var ry2 = ry * ry; ++ var _x2 = _x * _x; ++ var _y2 = _y * _y; ++ ++ // adjust radii ++ var l = _x2 / rx2 + _y2 / ry2; ++ if (l > 1) { ++ rx *= Math.sqrt(l); ++ ry *= Math.sqrt(l); ++ } ++ ++ var amp = Math.sqrt((rx2 * ry2 - rx2 * _y2 - ry2 * _x2) / (rx2 * _y2 + ry2 * _x2)); ++ ++ if (_.isNaN(amp)) { ++ amp = 0; ++ } else if (largeArcFlag != sweepFlag && amp > 0) { ++ amp *= -1; ++ } ++ ++ // Calculate cx' cy' F.6.5.2 ++ var _cx = amp * rx * _y / ry; ++ var _cy = - amp * ry * _x / rx; ++ ++ // Calculate cx cy F.6.5.3 ++ var cx = _cx * Math.cos(xAxisRotation) - _cy * Math.sin(xAxisRotation) + (x1 + x4) / 2; ++ var cy = _cx * Math.sin(xAxisRotation) + _cy * Math.cos(xAxisRotation) + (y1 + y4) / 2; ++ ++ // vector magnitude ++ var m = function(v) { return Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2)); } ++ // ratio between two vectors ++ var r = function(u, v) { return (u[0] * v[0] + u[1] * v[1]) / (m(u) * m(v)) } ++ // angle between two vectors ++ var a = function(u, v) { return (u[0] * v[1] < u[1] * v[0] ? - 1 : 1) * Math.acos(r(u,v)); } ++ ++ // Calculate theta1 and delta theta F.6.5.4 + F.6.5.5 ++ var t1 = a([1, 0], [(_x - _cx) / rx, (_y - _cy) / ry]); ++ var u = [(_x - _cx) / rx, (_y - _cy) / ry]; ++ var v = [( - _x - _cx) / rx, ( - _y - _cy) / ry]; ++ var dt = a(u, v); ++ ++ if (r(u, v) <= -1) dt = Math.PI; ++ if (r(u, v) >= 1) dt = 0; ++ ++ // F.6.5.6 ++ if (largeArcFlag) { ++ dt = mod(dt, Math.PI * 2); ++ } ++ ++ if (sweepFlag && dt > 0) { ++ dt -= Math.PI * 2; ++ } ++ ++ var length = Two.Resolution; ++ ++ // Save a projection of our rotation and translation to apply ++ // to the set of points. ++ var projection = new Two.Matrix() ++ .translate(cx, cy) ++ .rotate(xAxisRotation); ++ ++ // Create a resulting array of Two.Anchor's to export to the ++ // the path. ++ result = _.map(_.range(length), function(i) { ++ var pct = 1 - (i / (length - 1)); ++ var theta = pct * dt + t1; ++ var x = rx * Math.cos(theta); ++ var y = ry * Math.sin(theta); ++ var projected = projection.multiply(x, y, 1); ++ return new Two.Anchor(projected.x, projected.y, false, false, false, false, Two.Commands.line);; ++ }); ++ ++ result.push(new Two.Anchor(x4, y4, false, false, false, false, Two.Commands.line)); ++ ++ coord = result[result.length - 1]; ++ control = coord.controls.left; ++ ++ break; ++ ++ } ++ ++ if (result) { ++ if (_.isArray(result)) { ++ points = points.concat(result); ++ } else { ++ points.push(result); ++ } ++ } ++ ++ }); ++ ++ if (points.length <= 1) { ++ return; ++ } ++ ++ var path = new Two.Path(points, closed, undefined, true).noStroke(); ++ path.fill = 'black'; ++ ++ var rect = path.getBoundingClientRect(true); ++ ++ // Center objects to stay consistent ++ // with the rest of the Two.js API. ++ rect.centroid = { ++ x: rect.left + rect.width / 2, ++ y: rect.top + rect.height / 2 ++ }; ++ ++ _.each(path.vertices, function(v) { ++ v.subSelf(rect.centroid); ++ }); ++ ++ path.translation.addSelf(rect.centroid); ++ ++ return Two.Utils.applySvgAttributes.call(this, node, path); ++ ++ }, ++ ++ circle: function(node) { ++ ++ var x = parseFloat(node.getAttribute('cx')); ++ var y = parseFloat(node.getAttribute('cy')); ++ var r = parseFloat(node.getAttribute('r')); ++ ++ var circle = new Two.Circle(x, y, r).noStroke(); ++ circle.fill = 'black'; ++ ++ return Two.Utils.applySvgAttributes.call(this, node, circle); ++ ++ }, ++ ++ ellipse: function(node) { ++ ++ var x = parseFloat(node.getAttribute('cx')); ++ var y = parseFloat(node.getAttribute('cy')); ++ var width = parseFloat(node.getAttribute('rx')); ++ var height = parseFloat(node.getAttribute('ry')); ++ ++ var ellipse = new Two.Ellipse(x, y, width, height).noStroke(); ++ ellipse.fill = 'black'; ++ ++ return Two.Utils.applySvgAttributes.call(this, node, ellipse); ++ ++ }, ++ ++ rect: function(node) { ++ ++ var x = parseFloat(node.getAttribute('x')) || 0; ++ var y = parseFloat(node.getAttribute('y')) || 0; ++ var width = parseFloat(node.getAttribute('width')); ++ var height = parseFloat(node.getAttribute('height')); ++ ++ var w2 = width / 2; ++ var h2 = height / 2; ++ ++ var rect = new Two.Rectangle(x + w2, y + h2, width, height) ++ .noStroke(); ++ rect.fill = 'black'; ++ ++ return Two.Utils.applySvgAttributes.call(this, node, rect); ++ ++ }, ++ ++ line: function(node) { ++ ++ var x1 = parseFloat(node.getAttribute('x1')); ++ var y1 = parseFloat(node.getAttribute('y1')); ++ var x2 = parseFloat(node.getAttribute('x2')); ++ var y2 = parseFloat(node.getAttribute('y2')); ++ ++ var line = new Two.Line(x1, y1, x2, y2).noFill(); ++ ++ return Two.Utils.applySvgAttributes.call(this, node, line); ++ ++ }, ++ ++ lineargradient: function(node) { ++ ++ var x1 = parseFloat(node.getAttribute('x1')); ++ var y1 = parseFloat(node.getAttribute('y1')); ++ var x2 = parseFloat(node.getAttribute('x2')); ++ var y2 = parseFloat(node.getAttribute('y2')); ++ ++ var ox = (x2 + x1) / 2; ++ var oy = (y2 + y1) / 2; ++ ++ var stops = []; ++ for (var i = 0; i < node.children.length; i++) { ++ ++ var child = node.children[i]; ++ ++ var offset = parseFloat(child.getAttribute('offset')); ++ var color = child.getAttribute('stop-color'); ++ var opacity = child.getAttribute('stop-opacity'); ++ var style = child.getAttribute('style'); ++ ++ if (_.isNull(color)) { ++ var matches = style ? style.match(/stop\-color\:\s?([\#a-fA-F0-9]*)/) : false; ++ color = matches && matches.length > 1 ? matches[1] : undefined; ++ } ++ ++ if (_.isNull(opacity)) { ++ var matches = style ? style.match(/stop\-opacity\:\s?([0-9\.\-]*)/) : false; ++ opacity = matches && matches.length > 1 ? parseFloat(matches[1]) : 1; ++ } ++ ++ stops.push(new Two.Gradient.Stop(offset, color, opacity)); ++ ++ } ++ ++ var gradient = new Two.LinearGradient(x1 - ox, y1 - oy, x2 - ox, ++ y2 - oy, stops); ++ ++ return Two.Utils.applySvgAttributes.call(this, node, gradient); ++ ++ }, ++ ++ radialgradient: function(node) { ++ ++ var cx = parseFloat(node.getAttribute('cx')) || 0; ++ var cy = parseFloat(node.getAttribute('cy')) || 0; ++ var r = parseFloat(node.getAttribute('r')); ++ ++ var fx = parseFloat(node.getAttribute('fx')); ++ var fy = parseFloat(node.getAttribute('fy')); ++ ++ if (_.isNaN(fx)) { ++ fx = cx; ++ } ++ ++ if (_.isNaN(fy)) { ++ fy = cy; ++ } ++ ++ var ox = Math.abs(cx + fx) / 2; ++ var oy = Math.abs(cy + fy) / 2; ++ ++ var stops = []; ++ for (var i = 0; i < node.children.length; i++) { ++ ++ var child = node.children[i]; ++ ++ var offset = parseFloat(child.getAttribute('offset')); ++ var color = child.getAttribute('stop-color'); ++ var opacity = child.getAttribute('stop-opacity'); ++ var style = child.getAttribute('style'); ++ ++ if (_.isNull(color)) { ++ var matches = style ? style.match(/stop\-color\:\s?([\#a-fA-F0-9]*)/) : false; ++ color = matches && matches.length > 1 ? matches[1] : undefined; ++ } ++ ++ if (_.isNull(opacity)) { ++ var matches = style ? style.match(/stop\-opacity\:\s?([0-9\.\-]*)/) : false; ++ opacity = matches && matches.length > 1 ? parseFloat(matches[1]) : 1; ++ } ++ ++ stops.push(new Two.Gradient.Stop(offset, color, opacity)); ++ ++ } ++ ++ var gradient = new Two.RadialGradient(cx - ox, cy - oy, r, ++ stops, fx - ox, fy - oy); ++ ++ return Two.Utils.applySvgAttributes.call(this, node, gradient); ++ ++ } ++ ++ }, ++ ++ /** ++ * Given 2 points (a, b) and corresponding control point for each ++ * return an array of points that represent points plotted along ++ * the curve. Number points determined by limit. ++ */ ++ subdivide: function(x1, y1, x2, y2, x3, y3, x4, y4, limit) { ++ ++ limit = limit || Two.Utils.Curve.RecursionLimit; ++ var amount = limit + 1; ++ ++ // TODO: Issue 73 ++ // Don't recurse if the end points are identical ++ if (x1 === x4 && y1 === y4) { ++ return [new Two.Anchor(x4, y4)]; ++ } ++ ++ return _.map(_.range(0, amount), function(i) { ++ ++ var t = i / amount; ++ var x = getPointOnCubicBezier(t, x1, x2, x3, x4); ++ var y = getPointOnCubicBezier(t, y1, y2, y3, y4); ++ ++ return new Two.Anchor(x, y); ++ ++ }); ++ ++ }, ++ ++ getPointOnCubicBezier: function(t, a, b, c, d) { ++ var k = 1 - t; ++ return (k * k * k * a) + (3 * k * k * t * b) + (3 * k * t * t * c) + ++ (t * t * t * d); ++ }, ++ ++ /** ++ * Given 2 points (a, b) and corresponding control point for each ++ * return a float that represents the length of the curve using ++ * Gauss-Legendre algorithm. Limit iterations of calculation by `limit`. ++ */ ++ getCurveLength: function(x1, y1, x2, y2, x3, y3, x4, y4, limit) { ++ ++ // TODO: Better / fuzzier equality check ++ // Linear calculation ++ if (x1 === x2 && y1 === y2 && x3 === x4 && y3 === y4) { ++ var dx = x4 - x1; ++ var dy = y4 - y1; ++ return sqrt(dx * dx + dy * dy); ++ } ++ ++ // Calculate the coefficients of a Bezier derivative. ++ var ax = 9 * (x2 - x3) + 3 * (x4 - x1), ++ bx = 6 * (x1 + x3) - 12 * x2, ++ cx = 3 * (x2 - x1), ++ ++ ay = 9 * (y2 - y3) + 3 * (y4 - y1), ++ by = 6 * (y1 + y3) - 12 * y2, ++ cy = 3 * (y2 - y1); ++ ++ var integrand = function(t) { ++ // Calculate quadratic equations of derivatives for x and y ++ var dx = (ax * t + bx) * t + cx, ++ dy = (ay * t + by) * t + cy; ++ return sqrt(dx * dx + dy * dy); ++ }; ++ ++ return integrate( ++ integrand, 0, 1, limit || Two.Utils.Curve.RecursionLimit ++ ); ++ ++ }, ++ ++ /** ++ * Integration for `getCurveLength` calculations. Referenced from ++ * Paper.js: https://github.com/paperjs/paper.js/blob/master/src/util/Numerical.js#L101 ++ */ ++ integrate: function(f, a, b, n) { ++ var x = Two.Utils.Curve.abscissas[n - 2], ++ w = Two.Utils.Curve.weights[n - 2], ++ A = 0.5 * (b - a), ++ B = A + a, ++ i = 0, ++ m = (n + 1) >> 1, ++ sum = n & 1 ? w[i++] * f(B) : 0; // Handle odd n ++ while (i < m) { ++ var Ax = A * x[i]; ++ sum += w[i++] * (f(B + Ax) + f(B - Ax)); ++ } ++ return A * sum; ++ }, ++ ++ /** ++ * Creates a set of points that have u, v values for anchor positions ++ */ ++ getCurveFromPoints: function(points, closed) { ++ ++ var l = points.length, last = l - 1; ++ ++ for (var i = 0; i < l; i++) { ++ ++ var point = points[i]; ++ ++ if (!_.isObject(point.controls)) { ++ Two.Anchor.AppendCurveProperties(point); ++ } ++ ++ var prev = closed ? mod(i - 1, l) : max(i - 1, 0); ++ var next = closed ? mod(i + 1, l) : min(i + 1, last); ++ ++ var a = points[prev]; ++ var b = point; ++ var c = points[next]; ++ getControlPoints(a, b, c); ++ ++ b._command = i === 0 ? Two.Commands.move : Two.Commands.curve; ++ ++ b.controls.left.x = _.isNumber(b.controls.left.x) ? b.controls.left.x : b.x; ++ b.controls.left.y = _.isNumber(b.controls.left.y) ? b.controls.left.y : b.y; ++ ++ b.controls.right.x = _.isNumber(b.controls.right.x) ? b.controls.right.x : b.x; ++ b.controls.right.y = _.isNumber(b.controls.right.y) ? b.controls.right.y : b.y; ++ ++ } ++ ++ }, ++ ++ /** ++ * Given three coordinates return the control points for the middle, b, ++ * vertex. ++ */ ++ getControlPoints: function(a, b, c) { ++ ++ var a1 = angleBetween(a, b); ++ var a2 = angleBetween(c, b); ++ ++ var d1 = distanceBetween(a, b); ++ var d2 = distanceBetween(c, b); ++ ++ var mid = (a1 + a2) / 2; ++ ++ // So we know which angle corresponds to which side. ++ ++ b.u = _.isObject(b.controls.left) ? b.controls.left : new Two.Vector(0, 0); ++ b.v = _.isObject(b.controls.right) ? b.controls.right : new Two.Vector(0, 0); ++ ++ // TODO: Issue 73 ++ if (d1 < 0.0001 || d2 < 0.0001) { ++ if (!b._relative) { ++ b.controls.left.copy(b); ++ b.controls.right.copy(b); ++ } ++ return b; ++ } ++ ++ d1 *= 0.33; // Why 0.33? ++ d2 *= 0.33; ++ ++ if (a2 < a1) { ++ mid += HALF_PI; ++ } else { ++ mid -= HALF_PI; ++ } ++ ++ b.controls.left.x = cos(mid) * d1; ++ b.controls.left.y = sin(mid) * d1; ++ ++ mid -= PI; ++ ++ b.controls.right.x = cos(mid) * d2; ++ b.controls.right.y = sin(mid) * d2; ++ ++ if (!b._relative) { ++ b.controls.left.x += b.x; ++ b.controls.left.y += b.y; ++ b.controls.right.x += b.x; ++ b.controls.right.y += b.y; ++ } ++ ++ return b; ++ ++ }, ++ ++ /** ++ * Get the reflection of a point "b" about point "a". Where "a" is in ++ * absolute space and "b" is relative to "a". ++ * ++ * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes ++ */ ++ getReflection: function(a, b, relative) { ++ ++ return new Two.Vector( ++ 2 * a.x - (b.x + a.x) - (relative ? a.x : 0), ++ 2 * a.y - (b.y + a.y) - (relative ? a.y : 0) ++ ); ++ ++ }, ++ ++ getAnchorsFromArcData: function(center, xAxisRotation, rx, ry, ts, td, ccw) { ++ ++ var matrix = new Two.Matrix() ++ .translate(center.x, center.y) ++ .rotate(xAxisRotation); ++ ++ var l = Two.Resolution; ++ ++ return _.map(_.range(l), function(i) { ++ ++ var pct = (i + 1) / l; ++ if (!!ccw) { ++ pct = 1 - pct; ++ } ++ ++ var theta = pct * td + ts; ++ var x = rx * Math.cos(theta); ++ var y = ry * Math.sin(theta); ++ ++ // x += center.x; ++ // y += center.y; ++ ++ var anchor = new Two.Anchor(x, y); ++ Two.Anchor.AppendCurveProperties(anchor); ++ anchor.command = Two.Commands.line; ++ ++ // TODO: Calculate control points here... ++ ++ return anchor; ++ ++ }); ++ ++ }, ++ ++ ratioBetween: function(A, B) { ++ ++ return (A.x * B.x + A.y * B.y) / (A.length() * B.length()); ++ ++ }, ++ ++ angleBetween: function(A, B) { ++ ++ var dx, dy; ++ ++ if (arguments.length >= 4) { ++ ++ dx = arguments[0] - arguments[2]; ++ dy = arguments[1] - arguments[3]; ++ ++ return atan2(dy, dx); ++ ++ } ++ ++ dx = A.x - B.x; ++ dy = A.y - B.y; ++ ++ return atan2(dy, dx); ++ ++ }, ++ ++ distanceBetweenSquared: function(p1, p2) { ++ ++ var dx = p1.x - p2.x; ++ var dy = p1.y - p2.y; ++ ++ return dx * dx + dy * dy; ++ ++ }, ++ ++ distanceBetween: function(p1, p2) { ++ ++ return sqrt(distanceBetweenSquared(p1, p2)); ++ ++ }, ++ ++ lerp: function(a, b, t) { ++ return t * (b - a) + a; ++ }, ++ ++ // A pretty fast toFixed(3) alternative ++ // See http://jsperf.com/parsefloat-tofixed-vs-math-round/18 ++ toFixed: function(v) { ++ return Math.floor(v * 1000) / 1000; ++ }, ++ ++ mod: function(v, l) { ++ ++ while (v < 0) { ++ v += l; ++ } ++ ++ return v % l; ++ ++ }, ++ ++ /** ++ * Array like collection that triggers inserted and removed events ++ * removed : pop / shift / splice ++ * inserted : push / unshift / splice (with > 2 arguments) ++ */ ++ Collection: function() { ++ ++ Array.call(this); ++ ++ if (arguments.length > 1) { ++ Array.prototype.push.apply(this, arguments); ++ } else if (arguments[0] && Array.isArray(arguments[0])) { ++ Array.prototype.push.apply(this, arguments[0]); ++ } ++ ++ }, ++ ++ // Custom Error Throwing for Two.js ++ ++ Error: function(message) { ++ this.name = 'two.js'; ++ this.message = message; ++ }, ++ ++ Events: { ++ ++ on: function(name, callback) { ++ ++ this._events || (this._events = {}); ++ var list = this._events[name] || (this._events[name] = []); ++ ++ list.push(callback); ++ ++ return this; ++ ++ }, ++ ++ off: function(name, callback) { ++ ++ if (!this._events) { ++ return this; ++ } ++ if (!name && !callback) { ++ this._events = {}; ++ return this; ++ } ++ ++ var names = name ? [name] : _.keys(this._events); ++ for (var i = 0, l = names.length; i < l; i++) { ++ ++ var name = names[i]; ++ var list = this._events[name]; ++ ++ if (!!list) { ++ var events = []; ++ if (callback) { ++ for (var j = 0, k = list.length; j < k; j++) { ++ var ev = list[j]; ++ ev = ev.callback ? ev.callback : ev; ++ if (callback && callback !== ev) { ++ events.push(ev); ++ } ++ } ++ } ++ this._events[name] = events; ++ } ++ } ++ ++ return this; ++ }, ++ ++ trigger: function(name) { ++ if (!this._events) return this; ++ var args = slice.call(arguments, 1); ++ var events = this._events[name]; ++ if (events) trigger(this, events, args); ++ return this; ++ }, ++ ++ listen: function (obj, name, callback) { ++ ++ var bound = this; ++ ++ if (obj) { ++ var ev = function () { ++ callback.apply(bound, arguments); ++ }; ++ ++ // add references about the object that assigned this listener ++ ev.obj = obj; ++ ev.name = name; ++ ev.callback = callback; ++ ++ obj.on(name, ev); ++ } ++ ++ return this; ++ ++ }, ++ ++ ignore: function (obj, name, callback) { ++ ++ obj.off(name, callback); ++ ++ return this; ++ ++ } ++ ++ } ++ ++ }) ++ ++ }); ++ ++ Two.Utils.Events.bind = Two.Utils.Events.on; ++ Two.Utils.Events.unbind = Two.Utils.Events.off; ++ ++ var trigger = function(obj, events, args) { ++ var method; ++ switch (args.length) { ++ case 0: ++ method = function(i) { ++ events[i].call(obj, args[0]); ++ }; ++ break; ++ case 1: ++ method = function(i) { ++ events[i].call(obj, args[0], args[1]); ++ }; ++ break; ++ case 2: ++ method = function(i) { ++ events[i].call(obj, args[0], args[1], args[2]); ++ }; ++ break; ++ case 3: ++ method = function(i) { ++ events[i].call(obj, args[0], args[1], args[2], args[3]); ++ }; ++ break; ++ default: ++ method = function(i) { ++ events[i].apply(obj, args); ++ }; ++ } ++ for (var i = 0; i < events.length; i++) { ++ method(i); ++ } ++ }; ++ ++ Two.Utils.Error.prototype = new Error(); ++ Two.Utils.Error.prototype.constructor = Two.Utils.Error; ++ ++ Two.Utils.Collection.prototype = new Array(); ++ Two.Utils.Collection.prototype.constructor = Two.Utils.Collection; ++ ++ _.extend(Two.Utils.Collection.prototype, Two.Utils.Events, { ++ ++ pop: function() { ++ var popped = Array.prototype.pop.apply(this, arguments); ++ this.trigger(Two.Events.remove, [popped]); ++ return popped; ++ }, ++ ++ shift: function() { ++ var shifted = Array.prototype.shift.apply(this, arguments); ++ this.trigger(Two.Events.remove, [shifted]); ++ return shifted; ++ }, ++ ++ push: function() { ++ var pushed = Array.prototype.push.apply(this, arguments); ++ this.trigger(Two.Events.insert, arguments); ++ return pushed; ++ }, ++ ++ unshift: function() { ++ var unshifted = Array.prototype.unshift.apply(this, arguments); ++ this.trigger(Two.Events.insert, arguments); ++ return unshifted; ++ }, ++ ++ splice: function() { ++ var spliced = Array.prototype.splice.apply(this, arguments); ++ var inserted; ++ ++ this.trigger(Two.Events.remove, spliced); ++ ++ if (arguments.length > 2) { ++ inserted = this.slice(arguments[0], arguments[0] + arguments.length - 2); ++ this.trigger(Two.Events.insert, inserted); ++ this.trigger(Two.Events.order); ++ } ++ return spliced; ++ }, ++ ++ sort: function() { ++ Array.prototype.sort.apply(this, arguments); ++ this.trigger(Two.Events.order); ++ return this; ++ }, ++ ++ reverse: function() { ++ Array.prototype.reverse.apply(this, arguments); ++ this.trigger(Two.Events.order); ++ return this; ++ } ++ ++ }); ++ ++ // Localize utils ++ ++ var distanceBetween = Two.Utils.distanceBetween, ++ getAnchorsFromArcData = Two.Utils.getAnchorsFromArcData, ++ distanceBetweenSquared = Two.Utils.distanceBetweenSquared, ++ ratioBetween = Two.Utils.ratioBetween, ++ angleBetween = Two.Utils.angleBetween, ++ getControlPoints = Two.Utils.getControlPoints, ++ getCurveFromPoints = Two.Utils.getCurveFromPoints, ++ solveSegmentIntersection = Two.Utils.solveSegmentIntersection, ++ decoupleShapes = Two.Utils.decoupleShapes, ++ mod = Two.Utils.mod, ++ getBackingStoreRatio = Two.Utils.getBackingStoreRatio, ++ getPointOnCubicBezier = Two.Utils.getPointOnCubicBezier, ++ getCurveLength = Two.Utils.getCurveLength, ++ integrate = Two.Utils.integrate, ++ getReflection = Two.Utils.getReflection; ++ ++ _.extend(Two.prototype, Two.Utils.Events, { ++ ++ appendTo: function(elem) { ++ ++ elem.appendChild(this.renderer.domElement); ++ return this; ++ ++ }, ++ ++ play: function() { ++ ++ Two.Utils.setPlaying.call(this, true); ++ return this.trigger(Two.Events.play); ++ ++ }, ++ ++ pause: function() { ++ ++ this.playing = false; ++ return this.trigger(Two.Events.pause); ++ ++ }, ++ ++ /** ++ * Update positions and calculations in one pass before rendering. ++ */ ++ update: function() { ++ ++ var animated = !!this._lastFrame; ++ var now = perf.now(); ++ ++ this.frameCount++; ++ ++ if (animated) { ++ this.timeDelta = parseFloat((now - this._lastFrame).toFixed(3)); ++ } ++ this._lastFrame = now; ++ ++ var width = this.width; ++ var height = this.height; ++ var renderer = this.renderer; ++ ++ // Update width / height for the renderer ++ if (width !== renderer.width || height !== renderer.height) { ++ renderer.setSize(width, height, this.ratio); ++ } ++ ++ this.trigger(Two.Events.update, this.frameCount, this.timeDelta); ++ ++ return this.render(); ++ ++ }, ++ ++ /** ++ * Render all drawable - visible objects of the scene. ++ */ ++ render: function() { ++ ++ this.renderer.render(); ++ return this.trigger(Two.Events.render, this.frameCount); ++ ++ }, ++ ++ /** ++ * Convenience Methods ++ */ ++ ++ add: function(o) { ++ ++ var objects = o; ++ if (!(objects instanceof Array)) { ++ objects = _.toArray(arguments); ++ } ++ ++ this.scene.add(objects); ++ return this; ++ ++ }, ++ ++ remove: function(o) { ++ ++ var objects = o; ++ if (!(objects instanceof Array)) { ++ objects = _.toArray(arguments); ++ } ++ ++ this.scene.remove(objects); ++ ++ return this; ++ ++ }, ++ ++ clear: function() { ++ ++ this.scene.remove(_.toArray(this.scene.children)); ++ return this; ++ ++ }, ++ ++ makeLine: function(x1, y1, x2, y2) { ++ ++ var line = new Two.Line(x1, y1, x2, y2); ++ this.scene.add(line); ++ ++ return line; ++ ++ }, ++ ++ makeRectangle: function(x, y, width, height) { ++ ++ var rect = new Two.Rectangle(x, y, width, height); ++ this.scene.add(rect); ++ ++ return rect; ++ ++ }, ++ ++ makeRoundedRectangle: function(x, y, width, height, sides) { ++ ++ var rect = new Two.RoundedRectangle(x, y, width, height, sides); ++ this.scene.add(rect); ++ ++ return rect; ++ ++ }, ++ ++ makeCircle: function(ox, oy, r) { ++ ++ var circle = new Two.Circle(ox, oy, r); ++ this.scene.add(circle); ++ ++ return circle; ++ ++ }, ++ ++ makeEllipse: function(ox, oy, rx, ry) { ++ ++ var ellipse = new Two.Ellipse(ox, oy, rx, ry); ++ this.scene.add(ellipse); ++ ++ return ellipse; ++ ++ }, ++ ++ makeStar: function(ox, oy, or, ir, sides) { ++ ++ var star = new Two.Star(ox, oy, or, ir, sides); ++ this.scene.add(star); ++ ++ return star; ++ ++ }, ++ ++ makeCurve: function(p) { ++ ++ var l = arguments.length, points = p; ++ if (!_.isArray(p)) { ++ points = []; ++ for (var i = 0; i < l; i+=2) { ++ var x = arguments[i]; ++ if (!_.isNumber(x)) { ++ break; ++ } ++ var y = arguments[i + 1]; ++ points.push(new Two.Anchor(x, y)); ++ } ++ } ++ ++ var last = arguments[l - 1]; ++ var curve = new Two.Path(points, !(_.isBoolean(last) ? last : undefined), true); ++ var rect = curve.getBoundingClientRect(); ++ curve.center().translation ++ .set(rect.left + rect.width / 2, rect.top + rect.height / 2); ++ ++ this.scene.add(curve); ++ ++ return curve; ++ ++ }, ++ ++ makePolygon: function(ox, oy, r, sides) { ++ ++ var poly = new Two.Polygon(ox, oy, r, sides); ++ this.scene.add(poly); ++ ++ return poly; ++ ++ }, ++ ++ /* ++ * Make an Arc Segment ++ */ ++ ++ makeArcSegment: function(ox, oy, ir, or, sa, ea, res) { ++ var arcSegment = new Two.ArcSegment(ox, oy, ir, or, sa, ea, res); ++ this.scene.add(arcSegment); ++ return arcSegment; ++ }, ++ ++ /** ++ * Convenience method to make and draw a Two.Path. ++ */ ++ makePath: function(p) { ++ ++ var l = arguments.length, points = p; ++ if (!_.isArray(p)) { ++ points = []; ++ for (var i = 0; i < l; i+=2) { ++ var x = arguments[i]; ++ if (!_.isNumber(x)) { ++ break; ++ } ++ var y = arguments[i + 1]; ++ points.push(new Two.Anchor(x, y)); ++ } ++ } ++ ++ var last = arguments[l - 1]; ++ var path = new Two.Path(points, !(_.isBoolean(last) ? last : undefined)); ++ var rect = path.getBoundingClientRect(); ++ path.center().translation ++ .set(rect.left + rect.width / 2, rect.top + rect.height / 2); ++ ++ this.scene.add(path); ++ ++ return path; ++ ++ }, ++ ++ /** ++ * Convenience method to make and add a Two.Text. ++ */ ++ makeText: function(message, x, y, styles) { ++ var text = new Two.Text(message, x, y, styles); ++ this.add(text); ++ return text; ++ }, ++ ++ /** ++ * Convenience method to make and add a Two.LinearGradient. ++ */ ++ makeLinearGradient: function(x1, y1, x2, y2 /* stops */) { ++ ++ var stops = slice.call(arguments, 4); ++ var gradient = new Two.LinearGradient(x1, y1, x2, y2, stops); ++ ++ this.add(gradient); ++ ++ return gradient; ++ ++ }, ++ ++ /** ++ * Convenience method to make and add a Two.RadialGradient. ++ */ ++ makeRadialGradient: function(x1, y1, r /* stops */) { ++ ++ var stops = slice.call(arguments, 3); ++ var gradient = new Two.RadialGradient(x1, y1, r, stops); ++ ++ this.add(gradient); ++ ++ return gradient; ++ ++ }, ++ ++ makeSprite: function(path, x, y, cols, rows, frameRate, autostart) { ++ ++ var sprite = new Two.Sprite(path, x, y, cols, rows, frameRate); ++ if (!!autostart) { ++ sprite.play(); ++ } ++ this.add(sprite); ++ ++ return sprite; ++ ++ }, ++ ++ makeImageSequence: function(paths, x, y, frameRate, autostart) { ++ ++ var imageSequence = new Two.ImageSequence(paths, x, y, frameRate); ++ if (!!autostart) { ++ imageSequence.play(); ++ } ++ this.add(imageSequence); ++ ++ return imageSequence; ++ ++ }, ++ ++ makeTexture: function(path, callback) { ++ ++ var texture = new Two.Texture(path, callback); ++ return texture; ++ ++ }, ++ ++ makeGroup: function(o) { ++ ++ var objects = o; ++ if (!(objects instanceof Array)) { ++ objects = _.toArray(arguments); ++ } ++ ++ var group = new Two.Group(); ++ this.scene.add(group); ++ group.add(objects); ++ ++ return group; ++ ++ }, ++ ++ /** ++ * Interpret an SVG Node and add it to this instance's scene. The ++ * distinction should be made that this doesn't `import` svg's, it solely ++ * interprets them into something compatible for Two.js — this is slightly ++ * different than a direct transcription. ++ * ++ * @param {Object} svgNode - The node to be parsed ++ * @param {Boolean} shallow - Don't create a top-most group but ++ * append all contents directly ++ */ ++ interpret: function(svgNode, shallow) { ++ ++ var tag = svgNode.tagName.toLowerCase(); ++ ++ if (!(tag in Two.Utils.read)) { ++ return null; ++ } ++ ++ var node = Two.Utils.read[tag].call(this, svgNode); ++ ++ if (shallow && node instanceof Two.Group) { ++ this.add(node.children); ++ } else { ++ this.add(node); ++ } ++ ++ return node; ++ ++ }, ++ ++ /** ++ * Load an SVG file / text and interpret. ++ */ ++ load: function(text, callback) { ++ ++ var nodes = [], elem, i; ++ ++ if (/.*\.svg/ig.test(text)) { ++ ++ Two.Utils.xhr(text, _.bind(function(data) { ++ ++ dom.temp.innerHTML = data; ++ for (i = 0; i < dom.temp.children.length; i++) { ++ elem = dom.temp.children[i]; ++ nodes.push(this.interpret(elem)); ++ } ++ ++ callback(nodes.length <= 1 ? nodes[0] : nodes, ++ dom.temp.children.length <= 1 ? dom.temp.children[0] : dom.temp.children); ++ ++ }, this)); ++ ++ return this; ++ ++ } ++ ++ dom.temp.innerHTML = text; ++ for (i = 0; i < dom.temp.children.length; i++) { ++ elem = dom.temp.children[i]; ++ nodes.push(this.interpret(elem)); ++ } ++ ++ callback(nodes.length <= 1 ? nodes[0] : nodes, ++ dom.temp.children.length <= 1 ? dom.temp.children[0] : dom.temp.children); ++ ++ return this; ++ ++ } ++ ++ }); ++ ++ function fitToWindow() { ++ ++ var wr = document.body.getBoundingClientRect(); ++ ++ var width = this.width = wr.width; ++ var height = this.height = wr.height; ++ ++ this.renderer.setSize(width, height, this.ratio); ++ this.trigger(Two.Events.resize, width, height); ++ ++ } ++ ++ // Request Animation Frame ++ ++ var raf = dom.getRequestAnimationFrame(); ++ ++ function loop() { ++ ++ raf(loop); ++ ++ for (var i = 0; i < Two.Instances.length; i++) { ++ var t = Two.Instances[i]; ++ if (t.playing) { ++ t.update(); ++ } ++ } ++ ++ } ++ ++ if (typeof define === 'function' && define.amd) { ++ define('two', [], function() { ++ return Two; ++ }); ++ } else if (typeof module != 'undefined' && module.exports) { ++ module.exports = Two; ++ } ++ ++ return Two; ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ var _ = Two.Utils; ++ ++ var Registry = Two.Registry = function() { ++ ++ this.map = {}; ++ ++ }; ++ ++ _.extend(Registry, { ++ ++ }); ++ ++ _.extend(Registry.prototype, { ++ ++ add: function(id, obj) { ++ this.map[id] = obj; ++ return this; ++ }, ++ ++ remove: function(id) { ++ delete this.map[id]; ++ return this; ++ }, ++ ++ get: function(id) { ++ return this.map[id]; ++ }, ++ ++ contains: function(id) { ++ return id in this.map; ++ } ++ ++ }); ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ var _ = Two.Utils; ++ ++ var Vector = Two.Vector = function(x, y) { ++ ++ this.x = x || 0; ++ this.y = y || 0; ++ ++ }; ++ ++ _.extend(Vector, { ++ ++ zero: new Two.Vector() ++ ++ }); ++ ++ _.extend(Vector.prototype, Two.Utils.Events, { ++ ++ set: function(x, y) { ++ this.x = x; ++ this.y = y; ++ return this; ++ }, ++ ++ copy: function(v) { ++ this.x = v.x; ++ this.y = v.y; ++ return this; ++ }, ++ ++ clear: function() { ++ this.x = 0; ++ this.y = 0; ++ return this; ++ }, ++ ++ clone: function() { ++ return new Vector(this.x, this.y); ++ }, ++ ++ add: function(v1, v2) { ++ this.x = v1.x + v2.x; ++ this.y = v1.y + v2.y; ++ return this; ++ }, ++ ++ addSelf: function(v) { ++ this.x += v.x; ++ this.y += v.y; ++ return this; ++ }, ++ ++ sub: function(v1, v2) { ++ this.x = v1.x - v2.x; ++ this.y = v1.y - v2.y; ++ return this; ++ }, ++ ++ subSelf: function(v) { ++ this.x -= v.x; ++ this.y -= v.y; ++ return this; ++ }, ++ ++ multiplySelf: function(v) { ++ this.x *= v.x; ++ this.y *= v.y; ++ return this; ++ }, ++ ++ multiplyScalar: function(s) { ++ this.x *= s; ++ this.y *= s; ++ return this; ++ }, ++ ++ divideScalar: function(s) { ++ if (s) { ++ this.x /= s; ++ this.y /= s; ++ } else { ++ this.set(0, 0); ++ } ++ return this; ++ }, ++ ++ negate: function() { ++ return this.multiplyScalar(-1); ++ }, ++ ++ dot: function(v) { ++ return this.x * v.x + this.y * v.y; ++ }, ++ ++ lengthSquared: function() { ++ return this.x * this.x + this.y * this.y; ++ }, ++ ++ length: function() { ++ return Math.sqrt(this.lengthSquared()); ++ }, ++ ++ normalize: function() { ++ return this.divideScalar(this.length()); ++ }, ++ ++ distanceTo: function(v) { ++ return Math.sqrt(this.distanceToSquared(v)); ++ }, ++ ++ distanceToSquared: function(v) { ++ var dx = this.x - v.x, ++ dy = this.y - v.y; ++ return dx * dx + dy * dy; ++ }, ++ ++ setLength: function(l) { ++ return this.normalize().multiplyScalar(l); ++ }, ++ ++ equals: function(v, eps) { ++ eps = (typeof eps === 'undefined') ? 0.0001 : eps; ++ return (this.distanceTo(v) < eps); ++ }, ++ ++ lerp: function(v, t) { ++ var x = (v.x - this.x) * t + this.x; ++ var y = (v.y - this.y) * t + this.y; ++ return this.set(x, y); ++ }, ++ ++ isZero: function(eps) { ++ eps = (typeof eps === 'undefined') ? 0.0001 : eps; ++ return (this.length() < eps); ++ }, ++ ++ toString: function() { ++ return this.x + ', ' + this.y; ++ }, ++ ++ toObject: function() { ++ return { x: this.x, y: this.y }; ++ }, ++ ++ rotate: function (radians) { ++ var cos = Math.cos(radians); ++ var sin = Math.sin(radians); ++ this.x = this.x * cos - this.y * sin; ++ this.y = this.x * sin + this.y * cos; ++ return this; ++ } ++ ++ }); ++ ++ var BoundProto = { ++ ++ set: function(x, y) { ++ this._x = x; ++ this._y = y; ++ return this.trigger(Two.Events.change); ++ }, ++ ++ copy: function(v) { ++ this._x = v.x; ++ this._y = v.y; ++ return this.trigger(Two.Events.change); ++ }, ++ ++ clear: function() { ++ this._x = 0; ++ this._y = 0; ++ return this.trigger(Two.Events.change); ++ }, ++ ++ clone: function() { ++ return new Vector(this._x, this._y); ++ }, ++ ++ add: function(v1, v2) { ++ this._x = v1.x + v2.x; ++ this._y = v1.y + v2.y; ++ return this.trigger(Two.Events.change); ++ }, ++ ++ addSelf: function(v) { ++ this._x += v.x; ++ this._y += v.y; ++ return this.trigger(Two.Events.change); ++ }, ++ ++ sub: function(v1, v2) { ++ this._x = v1.x - v2.x; ++ this._y = v1.y - v2.y; ++ return this.trigger(Two.Events.change); ++ }, ++ ++ subSelf: function(v) { ++ this._x -= v.x; ++ this._y -= v.y; ++ return this.trigger(Two.Events.change); ++ }, ++ ++ multiplySelf: function(v) { ++ this._x *= v.x; ++ this._y *= v.y; ++ return this.trigger(Two.Events.change); ++ }, ++ ++ multiplyScalar: function(s) { ++ this._x *= s; ++ this._y *= s; ++ return this.trigger(Two.Events.change); ++ }, ++ ++ divideScalar: function(s) { ++ if (s) { ++ this._x /= s; ++ this._y /= s; ++ return this.trigger(Two.Events.change); ++ } ++ return this.clear(); ++ }, ++ ++ negate: function() { ++ return this.multiplyScalar(-1); ++ }, ++ ++ dot: function(v) { ++ return this._x * v.x + this._y * v.y; ++ }, ++ ++ lengthSquared: function() { ++ return this._x * this._x + this._y * this._y; ++ }, ++ ++ length: function() { ++ return Math.sqrt(this.lengthSquared()); ++ }, ++ ++ normalize: function() { ++ return this.divideScalar(this.length()); ++ }, ++ ++ distanceTo: function(v) { ++ return Math.sqrt(this.distanceToSquared(v)); ++ }, ++ ++ distanceToSquared: function(v) { ++ var dx = this._x - v.x, ++ dy = this._y - v.y; ++ return dx * dx + dy * dy; ++ }, ++ ++ setLength: function(l) { ++ return this.normalize().multiplyScalar(l); ++ }, ++ ++ equals: function(v, eps) { ++ eps = (typeof eps === 'undefined') ? 0.0001 : eps; ++ return (this.distanceTo(v) < eps); ++ }, ++ ++ lerp: function(v, t) { ++ var x = (v.x - this._x) * t + this._x; ++ var y = (v.y - this._y) * t + this._y; ++ return this.set(x, y); ++ }, ++ ++ isZero: function(eps) { ++ eps = (typeof eps === 'undefined') ? 0.0001 : eps; ++ return (this.length() < eps); ++ }, ++ ++ toString: function() { ++ return this._x + ', ' + this._y; ++ }, ++ ++ toObject: function() { ++ return { x: this._x, y: this._y }; ++ }, ++ ++ rotate: function (radians) { ++ var cos = Math.cos(radians); ++ var sin = Math.sin(radians); ++ this._x = this._x * cos - this._y * sin; ++ this._y = this._x * sin + this._y * cos; ++ return this; ++ } ++ ++ }; ++ ++ var xgs = { ++ enumerable: true, ++ get: function() { ++ return this._x; ++ }, ++ set: function(v) { ++ this._x = v; ++ this.trigger(Two.Events.change, 'x'); ++ } ++ }; ++ ++ var ygs = { ++ enumerable: true, ++ get: function() { ++ return this._y; ++ }, ++ set: function(v) { ++ this._y = v; ++ this.trigger(Two.Events.change, 'y'); ++ } ++ }; ++ ++ /** ++ * Override Backbone bind / on in order to add properly broadcasting. ++ * This allows Two.Vector to not broadcast events unless event listeners ++ * are explicity bound to it. ++ */ ++ ++ Two.Vector.prototype.bind = Two.Vector.prototype.on = function() { ++ ++ if (!this._bound) { ++ this._x = this.x; ++ this._y = this.y; ++ Object.defineProperty(this, 'x', xgs); ++ Object.defineProperty(this, 'y', ygs); ++ _.extend(this, BoundProto); ++ this._bound = true; // Reserved for event initialization check ++ } ++ ++ Two.Utils.Events.bind.apply(this, arguments); ++ ++ return this; ++ ++ }; ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ // Localized variables ++ var commands = Two.Commands; ++ var _ = Two.Utils; ++ ++ /** ++ * An object that holds 3 `Two.Vector`s, the anchor point and its ++ * corresponding handles: `left` and `right`. ++ */ ++ var Anchor = Two.Anchor = function(x, y, ux, uy, vx, vy, command) { ++ ++ Two.Vector.call(this, x, y); ++ ++ this._broadcast = _.bind(function() { ++ this.trigger(Two.Events.change); ++ }, this); ++ ++ this._command = command || commands.move; ++ this._relative = true; ++ ++ if (!command) { ++ return this; ++ } ++ ++ Anchor.AppendCurveProperties(this); ++ ++ if (_.isNumber(ux)) { ++ this.controls.left.x = ux; ++ } ++ if (_.isNumber(uy)) { ++ this.controls.left.y = uy; ++ } ++ if (_.isNumber(vx)) { ++ this.controls.right.x = vx; ++ } ++ if (_.isNumber(vy)) { ++ this.controls.right.y = vy; ++ } ++ ++ }; ++ ++ _.extend(Anchor, { ++ ++ AppendCurveProperties: function(anchor) { ++ anchor.controls = { ++ left: new Two.Vector(0, 0), ++ right: new Two.Vector(0, 0) ++ }; ++ } ++ ++ }); ++ ++ var AnchorProto = { ++ ++ listen: function() { ++ ++ if (!_.isObject(this.controls)) { ++ Anchor.AppendCurveProperties(this); ++ } ++ ++ this.controls.left.bind(Two.Events.change, this._broadcast); ++ this.controls.right.bind(Two.Events.change, this._broadcast); ++ ++ return this; ++ ++ }, ++ ++ ignore: function() { ++ ++ this.controls.left.unbind(Two.Events.change, this._broadcast); ++ this.controls.right.unbind(Two.Events.change, this._broadcast); ++ ++ return this; ++ ++ }, ++ ++ clone: function() { ++ ++ var controls = this.controls; ++ ++ var clone = new Two.Anchor( ++ this.x, ++ this.y, ++ controls && controls.left.x, ++ controls && controls.left.y, ++ controls && controls.right.x, ++ controls && controls.right.y, ++ this.command ++ ); ++ clone.relative = this._relative; ++ return clone; ++ ++ }, ++ ++ toObject: function() { ++ var o = { ++ x: this.x, ++ y: this.y ++ }; ++ if (this._command) { ++ o.command = this._command; ++ } ++ if (this._relative) { ++ o.relative = this._relative; ++ } ++ if (this.controls) { ++ o.controls = { ++ left: this.controls.left.toObject(), ++ right: this.controls.right.toObject() ++ }; ++ } ++ return o; ++ }, ++ ++ toString: function() { ++ if (!this.controls) { ++ return [this._x, this._y].join(', '); ++ } ++ return [this._x, this._y, this.controls.left.x, this.controls.left.y, ++ this.controls.right.x, this.controls.right.y].join(', '); ++ } ++ ++ }; ++ ++ Object.defineProperty(Anchor.prototype, 'command', { ++ ++ enumerable: true, ++ ++ get: function() { ++ return this._command; ++ }, ++ ++ set: function(c) { ++ this._command = c; ++ if (this._command === commands.curve && !_.isObject(this.controls)) { ++ Anchor.AppendCurveProperties(this); ++ } ++ return this.trigger(Two.Events.change); ++ } ++ ++ }); ++ ++ Object.defineProperty(Anchor.prototype, 'relative', { ++ ++ enumerable: true, ++ ++ get: function() { ++ return this._relative; ++ }, ++ ++ set: function(b) { ++ if (this._relative == b) { ++ return this; ++ } ++ this._relative = !!b; ++ return this.trigger(Two.Events.change); ++ } ++ ++ }); ++ ++ _.extend(Anchor.prototype, Two.Vector.prototype, AnchorProto); ++ ++ // Make it possible to bind and still have the Anchor specific ++ // inheritance from Two.Vector ++ Two.Anchor.prototype.bind = Two.Anchor.prototype.on = function() { ++ Two.Vector.prototype.bind.apply(this, arguments); ++ _.extend(this, AnchorProto); ++ }; ++ ++ Two.Anchor.prototype.unbind = Two.Anchor.prototype.off = function() { ++ Two.Vector.prototype.unbind.apply(this, arguments); ++ _.extend(this, AnchorProto); ++ }; ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ /** ++ * Constants ++ */ ++ var cos = Math.cos, sin = Math.sin, tan = Math.tan; ++ var _ = Two.Utils; ++ ++ /** ++ * Two.Matrix contains an array of elements that represent ++ * the two dimensional 3 x 3 matrix as illustrated below: ++ * ++ * ===== ++ * a b c ++ * d e f ++ * g h i // this row is not really used in 2d transformations ++ * ===== ++ * ++ * String order is for transform strings: a, d, b, e, c, f ++ * ++ * @class ++ */ ++ var Matrix = Two.Matrix = function(a, b, c, d, e, f) { ++ ++ this.elements = new Two.Array(9); ++ ++ var elements = a; ++ if (!_.isArray(elements)) { ++ elements = _.toArray(arguments); ++ } ++ ++ // initialize the elements with default values. ++ ++ this.identity().set(elements); ++ ++ }; ++ ++ _.extend(Matrix, { ++ ++ Identity: [ ++ 1, 0, 0, ++ 0, 1, 0, ++ 0, 0, 1 ++ ], ++ ++ /** ++ * Multiply two matrix 3x3 arrays ++ */ ++ Multiply: function(A, B, C) { ++ ++ if (B.length <= 3) { // Multiply Vector ++ ++ var x, y, z, e = A; ++ ++ var a = B[0] || 0, ++ b = B[1] || 0, ++ c = B[2] || 0; ++ ++ // Go down rows first ++ // a, d, g, b, e, h, c, f, i ++ ++ x = e[0] * a + e[1] * b + e[2] * c; ++ y = e[3] * a + e[4] * b + e[5] * c; ++ z = e[6] * a + e[7] * b + e[8] * c; ++ ++ return { x: x, y: y, z: z }; ++ ++ } ++ ++ var A0 = A[0], A1 = A[1], A2 = A[2]; ++ var A3 = A[3], A4 = A[4], A5 = A[5]; ++ var A6 = A[6], A7 = A[7], A8 = A[8]; ++ ++ var B0 = B[0], B1 = B[1], B2 = B[2]; ++ var B3 = B[3], B4 = B[4], B5 = B[5]; ++ var B6 = B[6], B7 = B[7], B8 = B[8]; ++ ++ C = C || new Two.Array(9); ++ ++ C[0] = A0 * B0 + A1 * B3 + A2 * B6; ++ C[1] = A0 * B1 + A1 * B4 + A2 * B7; ++ C[2] = A0 * B2 + A1 * B5 + A2 * B8; ++ C[3] = A3 * B0 + A4 * B3 + A5 * B6; ++ C[4] = A3 * B1 + A4 * B4 + A5 * B7; ++ C[5] = A3 * B2 + A4 * B5 + A5 * B8; ++ C[6] = A6 * B0 + A7 * B3 + A8 * B6; ++ C[7] = A6 * B1 + A7 * B4 + A8 * B7; ++ C[8] = A6 * B2 + A7 * B5 + A8 * B8; ++ ++ return C; ++ ++ } ++ ++ }); ++ ++ _.extend(Matrix.prototype, Two.Utils.Events, { ++ ++ /** ++ * Takes an array of elements or the arguments list itself to ++ * set and update the current matrix's elements. Only updates ++ * specified values. ++ */ ++ set: function(a) { ++ ++ var elements = a; ++ if (!_.isArray(elements)) { ++ elements = _.toArray(arguments); ++ } ++ ++ _.extend(this.elements, elements); ++ ++ return this.trigger(Two.Events.change); ++ ++ }, ++ ++ /** ++ * Turn matrix to identity, like resetting. ++ */ ++ identity: function() { ++ ++ this.set(Matrix.Identity); ++ ++ return this; ++ ++ }, ++ ++ /** ++ * Multiply scalar or multiply by another matrix. ++ */ ++ multiply: function(a, b, c, d, e, f, g, h, i) { ++ ++ var elements = arguments, l = elements.length; ++ ++ // Multiply scalar ++ ++ if (l <= 1) { ++ ++ _.each(this.elements, function(v, i) { ++ this.elements[i] = v * a; ++ }, this); ++ ++ return this.trigger(Two.Events.change); ++ ++ } ++ ++ if (l <= 3) { // Multiply Vector ++ ++ var x, y, z; ++ a = a || 0; ++ b = b || 0; ++ c = c || 0; ++ e = this.elements; ++ ++ // Go down rows first ++ // a, d, g, b, e, h, c, f, i ++ ++ x = e[0] * a + e[1] * b + e[2] * c; ++ y = e[3] * a + e[4] * b + e[5] * c; ++ z = e[6] * a + e[7] * b + e[8] * c; ++ ++ return { x: x, y: y, z: z }; ++ ++ } ++ ++ // Multiple matrix ++ ++ var A = this.elements; ++ var B = elements; ++ ++ var A0 = A[0], A1 = A[1], A2 = A[2]; ++ var A3 = A[3], A4 = A[4], A5 = A[5]; ++ var A6 = A[6], A7 = A[7], A8 = A[8]; ++ ++ var B0 = B[0], B1 = B[1], B2 = B[2]; ++ var B3 = B[3], B4 = B[4], B5 = B[5]; ++ var B6 = B[6], B7 = B[7], B8 = B[8]; ++ ++ this.elements[0] = A0 * B0 + A1 * B3 + A2 * B6; ++ this.elements[1] = A0 * B1 + A1 * B4 + A2 * B7; ++ this.elements[2] = A0 * B2 + A1 * B5 + A2 * B8; ++ ++ this.elements[3] = A3 * B0 + A4 * B3 + A5 * B6; ++ this.elements[4] = A3 * B1 + A4 * B4 + A5 * B7; ++ this.elements[5] = A3 * B2 + A4 * B5 + A5 * B8; ++ ++ this.elements[6] = A6 * B0 + A7 * B3 + A8 * B6; ++ this.elements[7] = A6 * B1 + A7 * B4 + A8 * B7; ++ this.elements[8] = A6 * B2 + A7 * B5 + A8 * B8; ++ ++ return this.trigger(Two.Events.change); ++ ++ }, ++ ++ inverse: function(out) { ++ ++ var a = this.elements; ++ out = out || new Two.Matrix(); ++ ++ var a00 = a[0], a01 = a[1], a02 = a[2]; ++ var a10 = a[3], a11 = a[4], a12 = a[5]; ++ var a20 = a[6], a21 = a[7], a22 = a[8]; ++ ++ var b01 = a22 * a11 - a12 * a21; ++ var b11 = -a22 * a10 + a12 * a20; ++ var b21 = a21 * a10 - a11 * a20; ++ ++ // Calculate the determinant ++ var det = a00 * b01 + a01 * b11 + a02 * b21; ++ ++ if (!det) { ++ return null; ++ } ++ ++ det = 1.0 / det; ++ ++ out.elements[0] = b01 * det; ++ out.elements[1] = (-a22 * a01 + a02 * a21) * det; ++ out.elements[2] = (a12 * a01 - a02 * a11) * det; ++ out.elements[3] = b11 * det; ++ out.elements[4] = (a22 * a00 - a02 * a20) * det; ++ out.elements[5] = (-a12 * a00 + a02 * a10) * det; ++ out.elements[6] = b21 * det; ++ out.elements[7] = (-a21 * a00 + a01 * a20) * det; ++ out.elements[8] = (a11 * a00 - a01 * a10) * det; ++ ++ return out; ++ ++ }, ++ ++ /** ++ * Set a scalar onto the matrix. ++ */ ++ scale: function(sx, sy) { ++ ++ var l = arguments.length; ++ if (l <= 1) { ++ sy = sx; ++ } ++ ++ return this.multiply(sx, 0, 0, 0, sy, 0, 0, 0, 1); ++ ++ }, ++ ++ /** ++ * Rotate the matrix. ++ */ ++ rotate: function(radians) { ++ ++ var c = cos(radians); ++ var s = sin(radians); ++ ++ return this.multiply(c, -s, 0, s, c, 0, 0, 0, 1); ++ ++ }, ++ ++ /** ++ * Translate the matrix. ++ */ ++ translate: function(x, y) { ++ ++ return this.multiply(1, 0, x, 0, 1, y, 0, 0, 1); ++ ++ }, ++ ++ /* ++ * Skew the matrix by an angle in the x axis direction. ++ */ ++ skewX: function(radians) { ++ ++ var a = tan(radians); ++ ++ return this.multiply(1, a, 0, 0, 1, 0, 0, 0, 1); ++ ++ }, ++ ++ /* ++ * Skew the matrix by an angle in the y axis direction. ++ */ ++ skewY: function(radians) { ++ ++ var a = tan(radians); ++ ++ return this.multiply(1, 0, 0, a, 1, 0, 0, 0, 1); ++ ++ }, ++ ++ /** ++ * Create a transform string to be used with rendering apis. ++ */ ++ toString: function(fullMatrix) { ++ var temp = []; ++ ++ this.toArray(fullMatrix, temp); ++ ++ return temp.join(' '); ++ ++ }, ++ ++ /** ++ * Create a transform array to be used with rendering apis. ++ */ ++ toArray: function(fullMatrix, output) { ++ ++ var elements = this.elements; ++ var hasOutput = !!output; ++ ++ var a = parseFloat(elements[0].toFixed(3)); ++ var b = parseFloat(elements[1].toFixed(3)); ++ var c = parseFloat(elements[2].toFixed(3)); ++ var d = parseFloat(elements[3].toFixed(3)); ++ var e = parseFloat(elements[4].toFixed(3)); ++ var f = parseFloat(elements[5].toFixed(3)); ++ ++ if (!!fullMatrix) { ++ ++ var g = parseFloat(elements[6].toFixed(3)); ++ var h = parseFloat(elements[7].toFixed(3)); ++ var i = parseFloat(elements[8].toFixed(3)); ++ ++ if (hasOutput) { ++ output[0] = a; ++ output[1] = d; ++ output[2] = g; ++ output[3] = b; ++ output[4] = e; ++ output[5] = h; ++ output[6] = c; ++ output[7] = f; ++ output[8] = i; ++ return; ++ } ++ ++ return [ ++ a, d, g, b, e, h, c, f, i ++ ]; ++ } ++ ++ if (hasOutput) { ++ output[0] = a; ++ output[1] = d; ++ output[2] = b; ++ output[3] = e; ++ output[4] = c; ++ output[5] = f; ++ return; ++ } ++ ++ return [ ++ a, d, b, e, c, f // Specific format see LN:19 ++ ]; ++ ++ }, ++ ++ /** ++ * Clone the current matrix. ++ */ ++ clone: function() { ++ var a, b, c, d, e, f, g, h, i; ++ ++ a = this.elements[0]; ++ b = this.elements[1]; ++ c = this.elements[2]; ++ d = this.elements[3]; ++ e = this.elements[4]; ++ f = this.elements[5]; ++ g = this.elements[6]; ++ h = this.elements[7]; ++ i = this.elements[8]; ++ ++ return new Two.Matrix(a, b, c, d, e, f, g, h, i); ++ ++ } ++ ++ }); ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ // Localize variables ++ var mod = Two.Utils.mod, toFixed = Two.Utils.toFixed; ++ var _ = Two.Utils; ++ ++ var svg = { ++ ++ version: 1.1, ++ ++ ns: 'http://www.w3.org/2000/svg', ++ xlink: 'http://www.w3.org/1999/xlink', ++ ++ alignments: { ++ left: 'start', ++ center: 'middle', ++ right: 'end' ++ }, ++ ++ /** ++ * Create an svg namespaced element. ++ */ ++ createElement: function(name, attrs) { ++ var tag = name; ++ var elem = document.createElementNS(svg.ns, tag); ++ if (tag === 'svg') { ++ attrs = _.defaults(attrs || {}, { ++ version: svg.version ++ }); ++ } ++ if (!_.isEmpty(attrs)) { ++ svg.setAttributes(elem, attrs); ++ } ++ return elem; ++ }, ++ ++ /** ++ * Add attributes from an svg element. ++ */ ++ setAttributes: function(elem, attrs) { ++ var keys = Object.keys(attrs); ++ for (var i = 0; i < keys.length; i++) { ++ if (/href/.test(keys[i])) { ++ elem.setAttributeNS(svg.xlink, keys[i], attrs[keys[i]]); ++ } else { ++ elem.setAttribute(keys[i], attrs[keys[i]]); ++ } ++ } ++ return this; ++ }, ++ ++ /** ++ * Remove attributes from an svg element. ++ */ ++ removeAttributes: function(elem, attrs) { ++ for (var key in attrs) { ++ elem.removeAttribute(key); ++ } ++ return this; ++ }, ++ ++ /** ++ * Turn a set of vertices into a string for the d property of a path ++ * element. It is imperative that the string collation is as fast as ++ * possible, because this call will be happening multiple times a ++ * second. ++ */ ++ toString: function(points, closed) { ++ ++ var l = points.length, ++ last = l - 1, ++ d, // The elusive last Two.Commands.move point ++ ret = ''; ++ ++ for (var i = 0; i < l; i++) { ++ var b = points[i]; ++ var command; ++ var prev = closed ? mod(i - 1, l) : Math.max(i - 1, 0); ++ var next = closed ? mod(i + 1, l) : Math.min(i + 1, last); ++ ++ var a = points[prev]; ++ var c = points[next]; ++ ++ var vx, vy, ux, uy, ar, bl, br, cl; ++ ++ // Access x and y directly, ++ // bypassing the getter ++ var x = toFixed(b._x); ++ var y = toFixed(b._y); ++ ++ switch (b._command) { ++ ++ case Two.Commands.close: ++ command = Two.Commands.close; ++ break; ++ ++ case Two.Commands.curve: ++ ++ ar = (a.controls && a.controls.right) || Two.Vector.zero; ++ bl = (b.controls && b.controls.left) || Two.Vector.zero; ++ ++ if (a._relative) { ++ vx = toFixed((ar.x + a.x)); ++ vy = toFixed((ar.y + a.y)); ++ } else { ++ vx = toFixed(ar.x); ++ vy = toFixed(ar.y); ++ } ++ ++ if (b._relative) { ++ ux = toFixed((bl.x + b.x)); ++ uy = toFixed((bl.y + b.y)); ++ } else { ++ ux = toFixed(bl.x); ++ uy = toFixed(bl.y); ++ } ++ ++ command = ((i === 0) ? Two.Commands.move : Two.Commands.curve) + ++ ' ' + vx + ' ' + vy + ' ' + ux + ' ' + uy + ' ' + x + ' ' + y; ++ break; ++ ++ case Two.Commands.move: ++ d = b; ++ command = Two.Commands.move + ' ' + x + ' ' + y; ++ break; ++ ++ default: ++ command = b._command + ' ' + x + ' ' + y; ++ ++ } ++ ++ // Add a final point and close it off ++ ++ if (i >= last && closed) { ++ ++ if (b._command === Two.Commands.curve) { ++ ++ // Make sure we close to the most previous Two.Commands.move ++ c = d; ++ ++ br = (b.controls && b.controls.right) || b; ++ cl = (c.controls && c.controls.left) || c; ++ ++ if (b._relative) { ++ vx = toFixed((br.x + b.x)); ++ vy = toFixed((br.y + b.y)); ++ } else { ++ vx = toFixed(br.x); ++ vy = toFixed(br.y); ++ } ++ ++ if (c._relative) { ++ ux = toFixed((cl.x + c.x)); ++ uy = toFixed((cl.y + c.y)); ++ } else { ++ ux = toFixed(cl.x); ++ uy = toFixed(cl.y); ++ } ++ ++ x = toFixed(c.x); ++ y = toFixed(c.y); ++ ++ command += ++ ' C ' + vx + ' ' + vy + ' ' + ux + ' ' + uy + ' ' + x + ' ' + y; ++ } ++ ++ command += ' Z'; ++ ++ } ++ ++ ret += command + ' '; ++ ++ } ++ ++ return ret; ++ ++ }, ++ ++ getClip: function(shape) { ++ ++ var clip = shape._renderer.clip; ++ ++ if (!clip) { ++ ++ var root = shape; ++ ++ while (root.parent) { ++ root = root.parent; ++ } ++ ++ clip = shape._renderer.clip = svg.createElement('clipPath'); ++ root.defs.appendChild(clip); ++ ++ } ++ ++ return clip; ++ ++ }, ++ ++ group: { ++ ++ // TODO: Can speed up. ++ // TODO: How does this effect a f ++ appendChild: function(object) { ++ ++ var elem = object._renderer.elem; ++ ++ if (!elem) { ++ return; ++ } ++ ++ var tag = elem.nodeName; ++ ++ if (!tag || /(radial|linear)gradient/i.test(tag) || object._clip) { ++ return; ++ } ++ ++ this.elem.appendChild(elem); ++ ++ }, ++ ++ removeChild: function(object) { ++ ++ var elem = object._renderer.elem; ++ ++ if (!elem || elem.parentNode != this.elem) { ++ return; ++ } ++ ++ var tag = elem.nodeName; ++ ++ if (!tag) { ++ return; ++ } ++ ++ // Defer subtractions while clipping. ++ if (object._clip) { ++ return; ++ } ++ ++ this.elem.removeChild(elem); ++ ++ }, ++ ++ orderChild: function(object) { ++ this.elem.appendChild(object._renderer.elem); ++ }, ++ ++ renderChild: function(child) { ++ svg[child._renderer.type].render.call(child, this); ++ }, ++ ++ render: function(domElement) { ++ ++ this._update(); ++ ++ // Shortcut for hidden objects. ++ // Doesn't reset the flags, so changes are stored and ++ // applied once the object is visible again ++ if (this._opacity === 0 && !this._flagOpacity) { ++ return this; ++ } ++ ++ if (!this._renderer.elem) { ++ this._renderer.elem = svg.createElement('g', { ++ id: this.id ++ }); ++ domElement.appendChild(this._renderer.elem); ++ } ++ ++ // _Update styles for the ++ var flagMatrix = this._matrix.manual || this._flagMatrix; ++ var context = { ++ domElement: domElement, ++ elem: this._renderer.elem ++ }; ++ ++ if (flagMatrix) { ++ this._renderer.elem.setAttribute('transform', 'matrix(' + this._matrix.toString() + ')'); ++ } ++ ++ for (var i = 0; i < this.children.length; i++) { ++ var child = this.children[i]; ++ svg[child._renderer.type].render.call(child, domElement); ++ } ++ ++ if (this._flagOpacity) { ++ this._renderer.elem.setAttribute('opacity', this._opacity); ++ } ++ ++ if (this._flagAdditions) { ++ this.additions.forEach(svg.group.appendChild, context); ++ } ++ ++ if (this._flagSubtractions) { ++ this.subtractions.forEach(svg.group.removeChild, context); ++ } ++ ++ if (this._flagOrder) { ++ this.children.forEach(svg.group.orderChild, context); ++ } ++ ++ /** ++ * Commented two-way functionality of clips / masks with groups and ++ * polygons. Uncomment when this bug is fixed: ++ * https://code.google.com/p/chromium/issues/detail?id=370951 ++ */ ++ ++ // if (this._flagClip) { ++ ++ // clip = svg.getClip(this); ++ // elem = this._renderer.elem; ++ ++ // if (this._clip) { ++ // elem.removeAttribute('id'); ++ // clip.setAttribute('id', this.id); ++ // clip.appendChild(elem); ++ // } else { ++ // clip.removeAttribute('id'); ++ // elem.setAttribute('id', this.id); ++ // this.parent._renderer.elem.appendChild(elem); // TODO: should be insertBefore ++ // } ++ ++ // } ++ ++ if (this._flagMask) { ++ if (this._mask) { ++ this._renderer.elem.setAttribute('clip-path', 'url(#' + this._mask.id + ')'); ++ } else { ++ this._renderer.elem.removeAttribute('clip-path'); ++ } ++ } ++ ++ return this.flagReset(); ++ ++ } ++ ++ }, ++ ++ path: { ++ ++ render: function(domElement) { ++ ++ this._update(); ++ ++ // Shortcut for hidden objects. ++ // Doesn't reset the flags, so changes are stored and ++ // applied once the object is visible again ++ if (this._opacity === 0 && !this._flagOpacity) { ++ return this; ++ } ++ ++ // Collect any attribute that needs to be changed here ++ var changed = {}; ++ ++ var flagMatrix = this._matrix.manual || this._flagMatrix; ++ ++ if (flagMatrix) { ++ changed.transform = 'matrix(' + this._matrix.toString() + ')'; ++ } ++ ++ if (this._flagVertices) { ++ var vertices = svg.toString(this._vertices, this._closed); ++ changed.d = vertices; ++ } ++ ++ if (this._fill && this._fill._renderer) { ++ this._fill._update(); ++ svg[this._fill._renderer.type].render.call(this._fill, domElement, true); ++ } ++ ++ if (this._flagFill) { ++ changed.fill = this._fill && this._fill.id ++ ? 'url(#' + this._fill.id + ')' : this._fill; ++ } ++ ++ if (this._stroke && this._stroke._renderer) { ++ this._stroke._update(); ++ svg[this._stroke._renderer.type].render.call(this._stroke, domElement, true); ++ } ++ ++ if (this._flagStroke) { ++ changed.stroke = this._stroke && this._stroke.id ++ ? 'url(#' + this._stroke.id + ')' : this._stroke; ++ } ++ ++ if (this._flagLinewidth) { ++ changed['stroke-width'] = this._linewidth; ++ } ++ ++ if (this._flagOpacity) { ++ changed['stroke-opacity'] = this._opacity; ++ changed['fill-opacity'] = this._opacity; ++ } ++ ++ if (this._flagVisible) { ++ changed.visibility = this._visible ? 'visible' : 'hidden'; ++ } ++ ++ if (this._flagCap) { ++ changed['stroke-linecap'] = this._cap; ++ } ++ ++ if (this._flagJoin) { ++ changed['stroke-linejoin'] = this._join; ++ } ++ ++ if (this._flagMiter) { ++ changed['stroke-miterlimit'] = this._miter; ++ } ++ ++ // If there is no attached DOM element yet, ++ // create it with all necessary attributes. ++ if (!this._renderer.elem) { ++ ++ changed.id = this.id; ++ this._renderer.elem = svg.createElement('path', changed); ++ domElement.appendChild(this._renderer.elem); ++ ++ // Otherwise apply all pending attributes ++ } else { ++ svg.setAttributes(this._renderer.elem, changed); ++ } ++ ++ if (this._flagClip) { ++ ++ var clip = svg.getClip(this); ++ var elem = this._renderer.elem; ++ ++ if (this._clip) { ++ elem.removeAttribute('id'); ++ clip.setAttribute('id', this.id); ++ clip.appendChild(elem); ++ } else { ++ clip.removeAttribute('id'); ++ elem.setAttribute('id', this.id); ++ this.parent._renderer.elem.appendChild(elem); // TODO: should be insertBefore ++ } ++ ++ } ++ ++ /** ++ * Commented two-way functionality of clips / masks with groups and ++ * polygons. Uncomment when this bug is fixed: ++ * https://code.google.com/p/chromium/issues/detail?id=370951 ++ */ ++ ++ // if (this._flagMask) { ++ // if (this._mask) { ++ // elem.setAttribute('clip-path', 'url(#' + this._mask.id + ')'); ++ // } else { ++ // elem.removeAttribute('clip-path'); ++ // } ++ // } ++ ++ return this.flagReset(); ++ ++ } ++ ++ }, ++ ++ text: { ++ ++ render: function(domElement) { ++ ++ this._update(); ++ ++ var changed = {}; ++ ++ var flagMatrix = this._matrix.manual || this._flagMatrix; ++ ++ if (flagMatrix) { ++ changed.transform = 'matrix(' + this._matrix.toString() + ')'; ++ } ++ ++ if (this._flagFamily) { ++ changed['font-family'] = this._family; ++ } ++ if (this._flagSize) { ++ changed['font-size'] = this._size; ++ } ++ if (this._flagLeading) { ++ changed['line-height'] = this._leading; ++ } ++ if (this._flagAlignment) { ++ changed['text-anchor'] = svg.alignments[this._alignment] || this._alignment; ++ } ++ if (this._flagBaseline) { ++ changed['alignment-baseline'] = changed['dominant-baseline'] = this._baseline; ++ } ++ if (this._flagStyle) { ++ changed['font-style'] = this._style; ++ } ++ if (this._flagWeight) { ++ changed['font-weight'] = this._weight; ++ } ++ if (this._flagDecoration) { ++ changed['text-decoration'] = this._decoration; ++ } ++ if (this._fill && this._fill._renderer) { ++ this._fill._update(); ++ svg[this._fill._renderer.type].render.call(this._fill, domElement, true); ++ } ++ if (this._flagFill) { ++ changed.fill = this._fill && this._fill.id ++ ? 'url(#' + this._fill.id + ')' : this._fill; ++ } ++ if (this._stroke && this._stroke._renderer) { ++ this._stroke._update(); ++ svg[this._stroke._renderer.type].render.call(this._stroke, domElement, true); ++ } ++ if (this._flagStroke) { ++ changed.stroke = this._stroke && this._stroke.id ++ ? 'url(#' + this._stroke.id + ')' : this._stroke; ++ } ++ if (this._flagLinewidth) { ++ changed['stroke-width'] = this._linewidth; ++ } ++ if (this._flagOpacity) { ++ changed.opacity = this._opacity; ++ } ++ if (this._flagVisible) { ++ changed.visibility = this._visible ? 'visible' : 'hidden'; ++ } ++ ++ if (!this._renderer.elem) { ++ ++ changed.id = this.id; ++ ++ this._renderer.elem = svg.createElement('text', changed); ++ domElement.defs.appendChild(this._renderer.elem); ++ ++ } else { ++ ++ svg.setAttributes(this._renderer.elem, changed); ++ ++ } ++ ++ if (this._flagClip) { ++ ++ var clip = svg.getClip(this); ++ var elem = this._renderer.elem; ++ ++ if (this._clip) { ++ elem.removeAttribute('id'); ++ clip.setAttribute('id', this.id); ++ clip.appendChild(elem); ++ } else { ++ clip.removeAttribute('id'); ++ elem.setAttribute('id', this.id); ++ this.parent._renderer.elem.appendChild(elem); // TODO: should be insertBefore ++ } ++ ++ } ++ ++ if (this._flagValue) { ++ this._renderer.elem.textContent = this._value; ++ } ++ ++ return this.flagReset(); ++ ++ } ++ ++ }, ++ ++ 'linear-gradient': { ++ ++ render: function(domElement, silent) { ++ ++ if (!silent) { ++ this._update(); ++ } ++ ++ var changed = {}; ++ ++ if (this._flagEndPoints) { ++ changed.x1 = this.left._x; ++ changed.y1 = this.left._y; ++ changed.x2 = this.right._x; ++ changed.y2 = this.right._y; ++ } ++ ++ if (this._flagSpread) { ++ changed.spreadMethod = this._spread; ++ } ++ ++ // If there is no attached DOM element yet, ++ // create it with all necessary attributes. ++ if (!this._renderer.elem) { ++ ++ changed.id = this.id; ++ changed.gradientUnits = 'userSpaceOnUse'; ++ this._renderer.elem = svg.createElement('linearGradient', changed); ++ domElement.defs.appendChild(this._renderer.elem); ++ ++ // Otherwise apply all pending attributes ++ } else { ++ ++ svg.setAttributes(this._renderer.elem, changed); ++ ++ } ++ ++ if (this._flagStops) { ++ ++ var lengthChanged = this._renderer.elem.childNodes.length ++ !== this.stops.length; ++ ++ if (lengthChanged) { ++ this._renderer.elem.childNodes.length = 0; ++ } ++ ++ for (var i = 0; i < this.stops.length; i++) { ++ ++ var stop = this.stops[i]; ++ var attrs = {}; ++ ++ if (stop._flagOffset) { ++ attrs.offset = 100 * stop._offset + '%'; ++ } ++ if (stop._flagColor) { ++ attrs['stop-color'] = stop._color; ++ } ++ if (stop._flagOpacity) { ++ attrs['stop-opacity'] = stop._opacity; ++ } ++ ++ if (!stop._renderer.elem) { ++ stop._renderer.elem = svg.createElement('stop', attrs); ++ } else { ++ svg.setAttributes(stop._renderer.elem, attrs); ++ } ++ ++ if (lengthChanged) { ++ this._renderer.elem.appendChild(stop._renderer.elem); ++ } ++ stop.flagReset(); ++ ++ } ++ ++ } ++ ++ return this.flagReset(); ++ ++ } ++ ++ }, ++ ++ 'radial-gradient': { ++ ++ render: function(domElement, silent) { ++ ++ if (!silent) { ++ this._update(); ++ } ++ ++ var changed = {}; ++ ++ if (this._flagCenter) { ++ changed.cx = this.center._x; ++ changed.cy = this.center._y; ++ } ++ if (this._flagFocal) { ++ changed.fx = this.focal._x; ++ changed.fy = this.focal._y; ++ } ++ ++ if (this._flagRadius) { ++ changed.r = this._radius; ++ } ++ ++ if (this._flagSpread) { ++ changed.spreadMethod = this._spread; ++ } ++ ++ // If there is no attached DOM element yet, ++ // create it with all necessary attributes. ++ if (!this._renderer.elem) { ++ ++ changed.id = this.id; ++ changed.gradientUnits = 'userSpaceOnUse'; ++ this._renderer.elem = svg.createElement('radialGradient', changed); ++ domElement.defs.appendChild(this._renderer.elem); ++ ++ // Otherwise apply all pending attributes ++ } else { ++ ++ svg.setAttributes(this._renderer.elem, changed); ++ ++ } ++ ++ if (this._flagStops) { ++ ++ var lengthChanged = this._renderer.elem.childNodes.length ++ !== this.stops.length; ++ ++ if (lengthChanged) { ++ this._renderer.elem.childNodes.length = 0; ++ } ++ ++ for (var i = 0; i < this.stops.length; i++) { ++ ++ var stop = this.stops[i]; ++ var attrs = {}; ++ ++ if (stop._flagOffset) { ++ attrs.offset = 100 * stop._offset + '%'; ++ } ++ if (stop._flagColor) { ++ attrs['stop-color'] = stop._color; ++ } ++ if (stop._flagOpacity) { ++ attrs['stop-opacity'] = stop._opacity; ++ } ++ ++ if (!stop._renderer.elem) { ++ stop._renderer.elem = svg.createElement('stop', attrs); ++ } else { ++ svg.setAttributes(stop._renderer.elem, attrs); ++ } ++ ++ if (lengthChanged) { ++ this._renderer.elem.appendChild(stop._renderer.elem); ++ } ++ stop.flagReset(); ++ ++ } ++ ++ } ++ ++ return this.flagReset(); ++ ++ } ++ ++ }, ++ ++ texture: { ++ ++ render: function(domElement, silent) { ++ ++ if (!silent) { ++ this._update(); ++ } ++ ++ var changed = {}; ++ var styles = { x: 0, y: 0 }; ++ var image = this.image; ++ ++ if (this._flagLoaded && this.loaded) { ++ ++ switch (image.nodeName.toLowerCase()) { ++ ++ case 'canvas': ++ styles.href = styles['xlink:href'] = image.toDataURL('image/png'); ++ break; ++ case 'img': ++ case 'image': ++ styles.href = styles['xlink:href'] = this.src; ++ break; ++ ++ } ++ ++ } ++ ++ if (this._flagOffset || this._flagLoaded || this._flagScale) { ++ ++ changed.x = this._offset.x; ++ changed.y = this._offset.y; ++ ++ if (image) { ++ ++ changed.x -= image.width / 2; ++ changed.y -= image.height / 2; ++ ++ if (this._scale instanceof Two.Vector) { ++ changed.x *= this._scale.x; ++ changed.y *= this._scale.y; ++ } else { ++ changed.x *= this._scale; ++ changed.y *= this._scale; ++ } ++ } ++ ++ if (changed.x > 0) { ++ changed.x *= - 1; ++ } ++ if (changed.y > 0) { ++ changed.y *= - 1; ++ } ++ ++ } ++ ++ if (this._flagScale || this._flagLoaded || this._flagRepeat) { ++ ++ changed.width = 0; ++ changed.height = 0; ++ ++ if (image) { ++ ++ styles.width = changed.width = image.width; ++ styles.height = changed.height = image.height; ++ ++ // TODO: Hack / Bandaid ++ switch (this._repeat) { ++ case 'no-repeat': ++ changed.width += 1; ++ changed.height += 1; ++ break; ++ } ++ ++ if (this._scale instanceof Two.Vector) { ++ changed.width *= this._scale.x; ++ changed.height *= this._scale.y; ++ } else { ++ changed.width *= this._scale; ++ changed.height *= this._scale; ++ } ++ } ++ ++ } ++ ++ if (this._flagScale || this._flagLoaded) { ++ if (!this._renderer.image) { ++ this._renderer.image = svg.createElement('image', styles); ++ } else if (!_.isEmpty(styles)) { ++ svg.setAttributes(this._renderer.image, styles); ++ } ++ } ++ ++ if (!this._renderer.elem) { ++ ++ changed.id = this.id; ++ changed.patternUnits = 'userSpaceOnUse'; ++ this._renderer.elem = svg.createElement('pattern', changed); ++ domElement.defs.appendChild(this._renderer.elem); ++ ++ } else if (!_.isEmpty(changed)) { ++ ++ svg.setAttributes(this._renderer.elem, changed); ++ ++ } ++ ++ if (this._renderer.elem && this._renderer.image && !this._renderer.appended) { ++ this._renderer.elem.appendChild(this._renderer.image); ++ this._renderer.appended = true; ++ } ++ ++ return this.flagReset(); ++ ++ } ++ ++ } ++ ++ }; ++ ++ /** ++ * @class ++ */ ++ var Renderer = Two[Two.Types.svg] = function(params) { ++ ++ this.domElement = params.domElement || svg.createElement('svg'); ++ ++ this.scene = new Two.Group(); ++ this.scene.parent = this; ++ ++ this.defs = svg.createElement('defs'); ++ this.domElement.appendChild(this.defs); ++ this.domElement.defs = this.defs; ++ this.domElement.style.overflow = 'hidden'; ++ ++ }; ++ ++ _.extend(Renderer, { ++ ++ Utils: svg ++ ++ }); ++ ++ _.extend(Renderer.prototype, Two.Utils.Events, { ++ ++ setSize: function(width, height) { ++ ++ this.width = width; ++ this.height = height; ++ ++ svg.setAttributes(this.domElement, { ++ width: width, ++ height: height ++ }); ++ ++ return this; ++ ++ }, ++ ++ render: function() { ++ ++ svg.group.render.call(this.scene, this.domElement); ++ ++ return this; ++ ++ } ++ ++ }); ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ /** ++ * Constants ++ */ ++ var mod = Two.Utils.mod, toFixed = Two.Utils.toFixed; ++ var getRatio = Two.Utils.getRatio; ++ var _ = Two.Utils; ++ ++ // Returns true if this is a non-transforming matrix ++ var isDefaultMatrix = function (m) { ++ return (m[0] == 1 && m[3] == 0 && m[1] == 0 && m[4] == 1 && m[2] == 0 && m[5] == 0); ++ }; ++ ++ var canvas = { ++ ++ isHidden: /(none|transparent)/i, ++ ++ alignments: { ++ left: 'start', ++ middle: 'center', ++ right: 'end' ++ }, ++ ++ shim: function(elem) { ++ elem.tagName = 'canvas'; ++ elem.nodeType = 1; ++ return elem; ++ }, ++ ++ group: { ++ ++ renderChild: function(child) { ++ canvas[child._renderer.type].render.call(child, this.ctx, true, this.clip); ++ }, ++ ++ render: function(ctx) { ++ ++ // TODO: Add a check here to only invoke _update if need be. ++ this._update(); ++ ++ var matrix = this._matrix.elements; ++ var parent = this.parent; ++ this._renderer.opacity = this._opacity * (parent && parent._renderer ? parent._renderer.opacity : 1); ++ ++ var defaultMatrix = isDefaultMatrix(matrix); ++ ++ var mask = this._mask; ++ // var clip = this._clip; ++ ++ if (!this._renderer.context) { ++ this._renderer.context = {}; ++ } ++ ++ this._renderer.context.ctx = ctx; ++ // this._renderer.context.clip = clip; ++ ++ if (!defaultMatrix) { ++ ctx.save(); ++ ctx.transform(matrix[0], matrix[3], matrix[1], matrix[4], matrix[2], matrix[5]); ++ } ++ ++ if (mask) { ++ canvas[mask._renderer.type].render.call(mask, ctx, true); ++ } ++ ++ if (this.opacity > 0 && this.scale !== 0) { ++ for (var i = 0; i < this.children.length; i++) { ++ var child = this.children[i]; ++ canvas[child._renderer.type].render.call(child, ctx); ++ } ++ } ++ ++ if (!defaultMatrix) { ++ ctx.restore(); ++ } ++ ++ /** ++ * Commented two-way functionality of clips / masks with groups and ++ * polygons. Uncomment when this bug is fixed: ++ * https://code.google.com/p/chromium/issues/detail?id=370951 ++ */ ++ ++ // if (clip) { ++ // ctx.clip(); ++ // } ++ ++ return this.flagReset(); ++ ++ } ++ ++ }, ++ ++ path: { ++ ++ render: function(ctx, forced, parentClipped) { ++ ++ var matrix, stroke, linewidth, fill, opacity, visible, cap, join, miter, ++ closed, commands, length, last, next, prev, a, b, c, d, ux, uy, vx, vy, ++ ar, bl, br, cl, x, y, mask, clip, defaultMatrix, isOffset; ++ ++ // TODO: Add a check here to only invoke _update if need be. ++ this._update(); ++ ++ matrix = this._matrix.elements; ++ stroke = this._stroke; ++ linewidth = this._linewidth; ++ fill = this._fill; ++ opacity = this._opacity * this.parent._renderer.opacity; ++ visible = this._visible; ++ cap = this._cap; ++ join = this._join; ++ miter = this._miter; ++ closed = this._closed; ++ commands = this._vertices; // Commands ++ length = commands.length; ++ last = length - 1; ++ defaultMatrix = isDefaultMatrix(matrix); ++ ++ // mask = this._mask; ++ clip = this._clip; ++ ++ if (!forced && (!visible || clip)) { ++ return this; ++ } ++ ++ // Transform ++ if (!defaultMatrix) { ++ ctx.save(); ++ ctx.transform(matrix[0], matrix[3], matrix[1], matrix[4], matrix[2], matrix[5]); ++ } ++ ++ /** ++ * Commented two-way functionality of clips / masks with groups and ++ * polygons. Uncomment when this bug is fixed: ++ * https://code.google.com/p/chromium/issues/detail?id=370951 ++ */ ++ ++ // if (mask) { ++ // canvas[mask._renderer.type].render.call(mask, ctx, true); ++ // } ++ ++ // Styles ++ if (fill) { ++ if (_.isString(fill)) { ++ ctx.fillStyle = fill; ++ } else { ++ canvas[fill._renderer.type].render.call(fill, ctx); ++ ctx.fillStyle = fill._renderer.effect; ++ } ++ } ++ if (stroke) { ++ if (_.isString(stroke)) { ++ ctx.strokeStyle = stroke; ++ } else { ++ canvas[stroke._renderer.type].render.call(stroke, ctx); ++ ctx.strokeStyle = stroke._renderer.effect; ++ } ++ } ++ if (linewidth) { ++ ctx.lineWidth = linewidth; ++ } ++ if (miter) { ++ ctx.miterLimit = miter; ++ } ++ if (join) { ++ ctx.lineJoin = join; ++ } ++ if (cap) { ++ ctx.lineCap = cap; ++ } ++ if (_.isNumber(opacity)) { ++ ctx.globalAlpha = opacity; ++ } ++ ++ ctx.beginPath(); ++ ++ for (var i = 0; i < commands.length; i++) { ++ ++ b = commands[i]; ++ ++ x = toFixed(b._x); ++ y = toFixed(b._y); ++ ++ switch (b._command) { ++ ++ case Two.Commands.close: ++ ctx.closePath(); ++ break; ++ ++ case Two.Commands.curve: ++ ++ prev = closed ? mod(i - 1, length) : Math.max(i - 1, 0); ++ next = closed ? mod(i + 1, length) : Math.min(i + 1, last); ++ ++ a = commands[prev]; ++ c = commands[next]; ++ ar = (a.controls && a.controls.right) || Two.Vector.zero; ++ bl = (b.controls && b.controls.left) || Two.Vector.zero; ++ ++ if (a._relative) { ++ vx = (ar.x + toFixed(a._x)); ++ vy = (ar.y + toFixed(a._y)); ++ } else { ++ vx = toFixed(ar.x); ++ vy = toFixed(ar.y); ++ } ++ ++ if (b._relative) { ++ ux = (bl.x + toFixed(b._x)); ++ uy = (bl.y + toFixed(b._y)); ++ } else { ++ ux = toFixed(bl.x); ++ uy = toFixed(bl.y); ++ } ++ ++ ctx.bezierCurveTo(vx, vy, ux, uy, x, y); ++ ++ if (i >= last && closed) { ++ ++ c = d; ++ ++ br = (b.controls && b.controls.right) || Two.Vector.zero; ++ cl = (c.controls && c.controls.left) || Two.Vector.zero; ++ ++ if (b._relative) { ++ vx = (br.x + toFixed(b._x)); ++ vy = (br.y + toFixed(b._y)); ++ } else { ++ vx = toFixed(br.x); ++ vy = toFixed(br.y); ++ } ++ ++ if (c._relative) { ++ ux = (cl.x + toFixed(c._x)); ++ uy = (cl.y + toFixed(c._y)); ++ } else { ++ ux = toFixed(cl.x); ++ uy = toFixed(cl.y); ++ } ++ ++ x = toFixed(c._x); ++ y = toFixed(c._y); ++ ++ ctx.bezierCurveTo(vx, vy, ux, uy, x, y); ++ ++ } ++ ++ break; ++ ++ case Two.Commands.line: ++ ctx.lineTo(x, y); ++ break; ++ ++ case Two.Commands.move: ++ d = b; ++ ctx.moveTo(x, y); ++ break; ++ ++ } ++ } ++ ++ // Loose ends ++ ++ if (closed) { ++ ctx.closePath(); ++ } ++ ++ if (!clip && !parentClipped) { ++ if (!canvas.isHidden.test(fill)) { ++ isOffset = fill._renderer && fill._renderer.offset ++ if (isOffset) { ++ ctx.save(); ++ ctx.translate( ++ - fill._renderer.offset.x, - fill._renderer.offset.y); ++ ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y); ++ } ++ ctx.fill(); ++ if (isOffset) { ++ ctx.restore(); ++ } ++ } ++ if (!canvas.isHidden.test(stroke)) { ++ isOffset = stroke._renderer && stroke._renderer.offset; ++ if (isOffset) { ++ ctx.save(); ++ ctx.translate( ++ - stroke._renderer.offset.x, - stroke._renderer.offset.y); ++ ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y); ++ ctx.lineWidth = linewidth / stroke._renderer.scale.x; ++ } ++ ctx.stroke(); ++ if (isOffset) { ++ ctx.restore(); ++ } ++ } ++ } ++ ++ if (!defaultMatrix) { ++ ctx.restore(); ++ } ++ ++ if (clip && !parentClipped) { ++ ctx.clip(); ++ } ++ ++ return this.flagReset(); ++ ++ } ++ ++ }, ++ ++ text: { ++ ++ render: function(ctx, forced, parentClipped) { ++ ++ // TODO: Add a check here to only invoke _update if need be. ++ this._update(); ++ ++ var matrix = this._matrix.elements; ++ var stroke = this._stroke; ++ var linewidth = this._linewidth; ++ var fill = this._fill; ++ var opacity = this._opacity * this.parent._renderer.opacity; ++ var visible = this._visible; ++ var defaultMatrix = isDefaultMatrix(matrix); ++ var isOffset = fill._renderer && fill._renderer.offset ++ && stroke._renderer && stroke._renderer.offset; ++ ++ var a, b, c, d, e, sx, sy; ++ ++ // mask = this._mask; ++ var clip = this._clip; ++ ++ if (!forced && (!visible || clip)) { ++ return this; ++ } ++ ++ // Transform ++ if (!defaultMatrix) { ++ ctx.save(); ++ ctx.transform(matrix[0], matrix[3], matrix[1], matrix[4], matrix[2], matrix[5]); ++ } ++ ++ /** ++ * Commented two-way functionality of clips / masks with groups and ++ * polygons. Uncomment when this bug is fixed: ++ * https://code.google.com/p/chromium/issues/detail?id=370951 ++ */ ++ ++ // if (mask) { ++ // canvas[mask._renderer.type].render.call(mask, ctx, true); ++ // } ++ ++ if (!isOffset) { ++ ctx.font = [this._style, this._weight, this._size + 'px/' + ++ this._leading + 'px', this._family].join(' '); ++ } ++ ++ ctx.textAlign = canvas.alignments[this._alignment] || this._alignment; ++ ctx.textBaseline = this._baseline; ++ ++ // Styles ++ if (fill) { ++ if (_.isString(fill)) { ++ ctx.fillStyle = fill; ++ } else { ++ canvas[fill._renderer.type].render.call(fill, ctx); ++ ctx.fillStyle = fill._renderer.effect; ++ } ++ } ++ if (stroke) { ++ if (_.isString(stroke)) { ++ ctx.strokeStyle = stroke; ++ } else { ++ canvas[stroke._renderer.type].render.call(stroke, ctx); ++ ctx.strokeStyle = stroke._renderer.effect; ++ } ++ } ++ if (linewidth) { ++ ctx.lineWidth = linewidth; ++ } ++ if (_.isNumber(opacity)) { ++ ctx.globalAlpha = opacity; ++ } ++ ++ if (!clip && !parentClipped) { ++ ++ if (!canvas.isHidden.test(fill)) { ++ ++ if (fill._renderer && fill._renderer.offset) { ++ ++ sx = toFixed(fill._renderer.scale.x); ++ sy = toFixed(fill._renderer.scale.y); ++ ++ ctx.save(); ++ ctx.translate( - toFixed(fill._renderer.offset.x), ++ - toFixed(fill._renderer.offset.y)); ++ ctx.scale(sx, sy); ++ ++ a = this._size / fill._renderer.scale.y; ++ b = this._leading / fill._renderer.scale.y; ++ ctx.font = [this._style, this._weight, toFixed(a) + 'px/', ++ toFixed(b) + 'px', this._family].join(' '); ++ ++ c = fill._renderer.offset.x / fill._renderer.scale.x; ++ d = fill._renderer.offset.y / fill._renderer.scale.y; ++ ++ ctx.fillText(this.value, toFixed(c), toFixed(d)); ++ ctx.restore(); ++ ++ } else { ++ ctx.fillText(this.value, 0, 0); ++ } ++ ++ } ++ ++ if (!canvas.isHidden.test(stroke)) { ++ ++ if (stroke._renderer && stroke._renderer.offset) { ++ ++ sx = toFixed(stroke._renderer.scale.x); ++ sy = toFixed(stroke._renderer.scale.y); ++ ++ ctx.save(); ++ ctx.translate(- toFixed(stroke._renderer.offset.x), ++ - toFixed(stroke._renderer.offset.y)); ++ ctx.scale(sx, sy); ++ ++ a = this._size / stroke._renderer.scale.y; ++ b = this._leading / stroke._renderer.scale.y; ++ ctx.font = [this._style, this._weight, toFixed(a) + 'px/', ++ toFixed(b) + 'px', this._family].join(' '); ++ ++ c = stroke._renderer.offset.x / stroke._renderer.scale.x; ++ d = stroke._renderer.offset.y / stroke._renderer.scale.y; ++ e = linewidth / stroke._renderer.scale.x; ++ ++ ctx.lineWidth = toFixed(e); ++ ctx.strokeText(this.value, toFixed(c), toFixed(d)); ++ ctx.restore(); ++ ++ } else { ++ ctx.strokeText(this.value, 0, 0); ++ } ++ } ++ } ++ ++ if (!defaultMatrix) { ++ ctx.restore(); ++ } ++ ++ // TODO: Test for text ++ if (clip && !parentClipped) { ++ ctx.clip(); ++ } ++ ++ return this.flagReset(); ++ ++ } ++ ++ }, ++ ++ 'linear-gradient': { ++ ++ render: function(ctx) { ++ ++ this._update(); ++ ++ if (!this._renderer.effect || this._flagEndPoints || this._flagStops) { ++ ++ this._renderer.effect = ctx.createLinearGradient( ++ this.left._x, this.left._y, ++ this.right._x, this.right._y ++ ); ++ ++ for (var i = 0; i < this.stops.length; i++) { ++ var stop = this.stops[i]; ++ this._renderer.effect.addColorStop(stop._offset, stop._color); ++ } ++ ++ } ++ ++ return this.flagReset(); ++ ++ } ++ ++ }, ++ ++ 'radial-gradient': { ++ ++ render: function(ctx) { ++ ++ this._update(); ++ ++ if (!this._renderer.effect || this._flagCenter || this._flagFocal ++ || this._flagRadius || this._flagStops) { ++ ++ this._renderer.effect = ctx.createRadialGradient( ++ this.center._x, this.center._y, 0, ++ this.focal._x, this.focal._y, this._radius ++ ); ++ ++ for (var i = 0; i < this.stops.length; i++) { ++ var stop = this.stops[i]; ++ this._renderer.effect.addColorStop(stop._offset, stop._color); ++ } ++ ++ } ++ ++ return this.flagReset(); ++ ++ } ++ ++ }, ++ ++ texture: { ++ ++ render: function(ctx) { ++ ++ this._update(); ++ ++ var image = this.image; ++ var repeat; ++ ++ if (!this._renderer.effect || ((this._flagLoaded || this._flagImage || this._flagVideo || this._flagRepeat) && this.loaded)) { ++ this._renderer.effect = ctx.createPattern(this.image, this._repeat); ++ } ++ ++ if (this._flagOffset || this._flagLoaded || this._flagScale) { ++ ++ if (!(this._renderer.offset instanceof Two.Vector)) { ++ this._renderer.offset = new Two.Vector(); ++ } ++ ++ this._renderer.offset.x = - this._offset.x; ++ this._renderer.offset.y = - this._offset.y; ++ ++ if (image) { ++ ++ this._renderer.offset.x += image.width / 2; ++ this._renderer.offset.y += image.height / 2; ++ ++ if (this._scale instanceof Two.Vector) { ++ this._renderer.offset.x *= this._scale.x; ++ this._renderer.offset.y *= this._scale.y; ++ } else { ++ this._renderer.offset.x *= this._scale; ++ this._renderer.offset.y *= this._scale; ++ } ++ } ++ ++ } ++ ++ if (this._flagScale || this._flagLoaded) { ++ ++ if (!(this._renderer.scale instanceof Two.Vector)) { ++ this._renderer.scale = new Two.Vector(); ++ } ++ ++ if (this._scale instanceof Two.Vector) { ++ this._renderer.scale.copy(this._scale); ++ } else { ++ this._renderer.scale.set(this._scale, this._scale); ++ } ++ ++ } ++ ++ return this.flagReset(); ++ ++ } ++ ++ } ++ ++ }; ++ ++ var Renderer = Two[Two.Types.canvas] = function(params) { ++ // Smoothing property. Defaults to true ++ // Set it to false when working with pixel art. ++ // false can lead to better performance, since it would use a cheaper interpolation algorithm. ++ // It might not make a big difference on GPU backed canvases. ++ var smoothing = (params.smoothing !== false); ++ this.domElement = params.domElement || document.createElement('canvas'); ++ this.ctx = this.domElement.getContext('2d'); ++ this.overdraw = params.overdraw || false; ++ ++ if (!_.isUndefined(this.ctx.imageSmoothingEnabled)) { ++ this.ctx.imageSmoothingEnabled = smoothing; ++ } ++ ++ // Everything drawn on the canvas needs to be added to the scene. ++ this.scene = new Two.Group(); ++ this.scene.parent = this; ++ }; ++ ++ ++ _.extend(Renderer, { ++ ++ Utils: canvas ++ ++ }); ++ ++ _.extend(Renderer.prototype, Two.Utils.Events, { ++ ++ setSize: function(width, height, ratio) { ++ ++ this.width = width; ++ this.height = height; ++ ++ this.ratio = _.isUndefined(ratio) ? getRatio(this.ctx) : ratio; ++ ++ this.domElement.width = width * this.ratio; ++ this.domElement.height = height * this.ratio; ++ ++ if (this.domElement.style) { ++ _.extend(this.domElement.style, { ++ width: width + 'px', ++ height: height + 'px' ++ }); ++ } ++ ++ return this; ++ ++ }, ++ ++ render: function() { ++ ++ var isOne = this.ratio === 1; ++ ++ if (!isOne) { ++ this.ctx.save(); ++ this.ctx.scale(this.ratio, this.ratio); ++ } ++ ++ if (!this.overdraw) { ++ this.ctx.clearRect(0, 0, this.width, this.height); ++ } ++ ++ canvas.group.render.call(this.scene, this.ctx); ++ ++ if (!isOne) { ++ this.ctx.restore(); ++ } ++ ++ return this; ++ ++ } ++ ++ }); ++ ++ function resetTransform(ctx) { ++ ctx.setTransform(1, 0, 0, 1, 0, 0); ++ } ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ /** ++ * Constants ++ */ ++ ++ var root = Two.root, ++ multiplyMatrix = Two.Matrix.Multiply, ++ mod = Two.Utils.mod, ++ identity = [1, 0, 0, 0, 1, 0, 0, 0, 1], ++ transformation = new Two.Array(9), ++ getRatio = Two.Utils.getRatio, ++ getComputedMatrix = Two.Utils.getComputedMatrix, ++ toFixed = Two.Utils.toFixed, ++ _ = Two.Utils; ++ ++ var webgl = { ++ ++ isHidden: /(none|transparent)/i, ++ ++ canvas: (root.document ? root.document.createElement('canvas') : { getContext: _.identity }), ++ ++ alignments: { ++ left: 'start', ++ middle: 'center', ++ right: 'end' ++ }, ++ ++ matrix: new Two.Matrix(), ++ ++ uv: new Two.Array([ ++ 0, 0, ++ 1, 0, ++ 0, 1, ++ 0, 1, ++ 1, 0, ++ 1, 1 ++ ]), ++ ++ group: { ++ ++ removeChild: function(child, gl) { ++ if (child.children) { ++ for (var i = 0; i < child.children.length; i++) { ++ webgl.group.removeChild(child.children[i], gl); ++ } ++ return; ++ } ++ // Deallocate texture to free up gl memory. ++ gl.deleteTexture(child._renderer.texture); ++ delete child._renderer.texture; ++ }, ++ ++ renderChild: function(child) { ++ webgl[child._renderer.type].render.call(child, this.gl, this.program); ++ }, ++ ++ render: function(gl, program) { ++ ++ this._update(); ++ ++ var parent = this.parent; ++ var flagParentMatrix = (parent._matrix && parent._matrix.manual) || parent._flagMatrix; ++ var flagMatrix = this._matrix.manual || this._flagMatrix; ++ ++ if (flagParentMatrix || flagMatrix) { ++ ++ if (!this._renderer.matrix) { ++ this._renderer.matrix = new Two.Array(9); ++ } ++ ++ // Reduce amount of object / array creation / deletion ++ this._matrix.toArray(true, transformation); ++ ++ multiplyMatrix(transformation, parent._renderer.matrix, this._renderer.matrix); ++ this._renderer.scale = this._scale * parent._renderer.scale; ++ ++ if (flagParentMatrix) { ++ this._flagMatrix = true; ++ } ++ ++ } ++ ++ if (this._mask) { ++ ++ gl.enable(gl.STENCIL_TEST); ++ gl.stencilFunc(gl.ALWAYS, 1, 1); ++ ++ gl.colorMask(false, false, false, true); ++ gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); ++ ++ webgl[this._mask._renderer.type].render.call(this._mask, gl, program, this); ++ ++ gl.colorMask(true, true, true, true); ++ gl.stencilFunc(gl.NOTEQUAL, 0, 1); ++ gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); ++ ++ } ++ ++ this._flagOpacity = parent._flagOpacity || this._flagOpacity; ++ ++ this._renderer.opacity = this._opacity ++ * (parent && parent._renderer ? parent._renderer.opacity : 1); ++ ++ if (this._flagSubtractions) { ++ for (var i = 0; i < this.subtractions.length; i++) { ++ webgl.group.removeChild(this.subtractions[i], gl); ++ } ++ } ++ ++ this.children.forEach(webgl.group.renderChild, { ++ gl: gl, ++ program: program ++ }); ++ ++ if (this._mask) { ++ ++ gl.colorMask(false, false, false, false); ++ gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); ++ ++ webgl[this._mask._renderer.type].render.call(this._mask, gl, program, this); ++ ++ gl.colorMask(true, true, true, true); ++ gl.stencilFunc(gl.NOTEQUAL, 0, 1); ++ gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); ++ ++ gl.disable(gl.STENCIL_TEST); ++ ++ } ++ ++ return this.flagReset(); ++ ++ } ++ ++ }, ++ ++ path: { ++ ++ updateCanvas: function(elem) { ++ ++ var next, prev, a, c, ux, uy, vx, vy, ar, bl, br, cl, x, y; ++ var isOffset; ++ ++ var commands = elem._vertices; ++ var canvas = this.canvas; ++ var ctx = this.ctx; ++ ++ // Styles ++ var scale = elem._renderer.scale; ++ var stroke = elem._stroke; ++ var linewidth = elem._linewidth; ++ var fill = elem._fill; ++ var opacity = elem._renderer.opacity || elem._opacity; ++ var cap = elem._cap; ++ var join = elem._join; ++ var miter = elem._miter; ++ var closed = elem._closed; ++ var length = commands.length; ++ var last = length - 1; ++ ++ canvas.width = Math.max(Math.ceil(elem._renderer.rect.width * scale), 1); ++ canvas.height = Math.max(Math.ceil(elem._renderer.rect.height * scale), 1); ++ ++ var centroid = elem._renderer.rect.centroid; ++ var cx = centroid.x; ++ var cy = centroid.y; ++ ++ ctx.clearRect(0, 0, canvas.width, canvas.height); ++ ++ if (fill) { ++ if (_.isString(fill)) { ++ ctx.fillStyle = fill; ++ } else { ++ webgl[fill._renderer.type].render.call(fill, ctx, elem); ++ ctx.fillStyle = fill._renderer.effect; ++ } ++ } ++ if (stroke) { ++ if (_.isString(stroke)) { ++ ctx.strokeStyle = stroke; ++ } else { ++ webgl[stroke._renderer.type].render.call(stroke, ctx, elem); ++ ctx.strokeStyle = stroke._renderer.effect; ++ } ++ } ++ if (linewidth) { ++ ctx.lineWidth = linewidth; ++ } ++ if (miter) { ++ ctx.miterLimit = miter; ++ } ++ if (join) { ++ ctx.lineJoin = join; ++ } ++ if (cap) { ++ ctx.lineCap = cap; ++ } ++ if (_.isNumber(opacity)) { ++ ctx.globalAlpha = opacity; ++ } ++ ++ var d; ++ ctx.save(); ++ ctx.scale(scale, scale); ++ ctx.translate(cx, cy); ++ ++ ctx.beginPath(); ++ for (var i = 0; i < commands.length; i++) { ++ ++ b = commands[i]; ++ ++ x = toFixed(b._x); ++ y = toFixed(b._y); ++ ++ switch (b._command) { ++ ++ case Two.Commands.close: ++ ctx.closePath(); ++ break; ++ ++ case Two.Commands.curve: ++ ++ prev = closed ? mod(i - 1, length) : Math.max(i - 1, 0); ++ next = closed ? mod(i + 1, length) : Math.min(i + 1, last); ++ ++ a = commands[prev]; ++ c = commands[next]; ++ ar = (a.controls && a.controls.right) || Two.Vector.zero; ++ bl = (b.controls && b.controls.left) || Two.Vector.zero; ++ ++ if (a._relative) { ++ vx = toFixed((ar.x + a._x)); ++ vy = toFixed((ar.y + a._y)); ++ } else { ++ vx = toFixed(ar.x); ++ vy = toFixed(ar.y); ++ } ++ ++ if (b._relative) { ++ ux = toFixed((bl.x + b._x)); ++ uy = toFixed((bl.y + b._y)); ++ } else { ++ ux = toFixed(bl.x); ++ uy = toFixed(bl.y); ++ } ++ ++ ctx.bezierCurveTo(vx, vy, ux, uy, x, y); ++ ++ if (i >= last && closed) { ++ ++ c = d; ++ ++ br = (b.controls && b.controls.right) || Two.Vector.zero; ++ cl = (c.controls && c.controls.left) || Two.Vector.zero; ++ ++ if (b._relative) { ++ vx = toFixed((br.x + b._x)); ++ vy = toFixed((br.y + b._y)); ++ } else { ++ vx = toFixed(br.x); ++ vy = toFixed(br.y); ++ } ++ ++ if (c._relative) { ++ ux = toFixed((cl.x + c._x)); ++ uy = toFixed((cl.y + c._y)); ++ } else { ++ ux = toFixed(cl.x); ++ uy = toFixed(cl.y); ++ } ++ ++ x = toFixed(c._x); ++ y = toFixed(c._y); ++ ++ ctx.bezierCurveTo(vx, vy, ux, uy, x, y); ++ ++ } ++ ++ break; ++ ++ case Two.Commands.line: ++ ctx.lineTo(x, y); ++ break; ++ ++ case Two.Commands.move: ++ d = b; ++ ctx.moveTo(x, y); ++ break; ++ ++ } ++ ++ } ++ ++ // Loose ends ++ ++ if (closed) { ++ ctx.closePath(); ++ } ++ ++ if (!webgl.isHidden.test(fill)) { ++ isOffset = fill._renderer && fill._renderer.offset ++ if (isOffset) { ++ ctx.save(); ++ ctx.translate( ++ - fill._renderer.offset.x, - fill._renderer.offset.y); ++ ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y); ++ } ++ ctx.fill(); ++ if (isOffset) { ++ ctx.restore(); ++ } ++ } ++ ++ if (!webgl.isHidden.test(stroke)) { ++ isOffset = stroke._renderer && stroke._renderer.offset; ++ if (isOffset) { ++ ctx.save(); ++ ctx.translate( ++ - stroke._renderer.offset.x, - stroke._renderer.offset.y); ++ ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y); ++ ctx.lineWidth = linewidth / stroke._renderer.scale.x; ++ } ++ ctx.stroke(); ++ if (isOffset) { ++ ctx.restore(); ++ } ++ } ++ ++ ctx.restore(); ++ ++ }, ++ ++ /** ++ * Returns the rect of a set of verts. Typically takes vertices that are ++ * "centered" around 0 and returns them to be anchored upper-left. ++ */ ++ getBoundingClientRect: function(vertices, border, rect) { ++ ++ var left = Infinity, right = -Infinity, ++ top = Infinity, bottom = -Infinity, ++ width, height; ++ ++ vertices.forEach(function(v) { ++ ++ var x = v.x, y = v.y, controls = v.controls; ++ var a, b, c, d, cl, cr; ++ ++ top = Math.min(y, top); ++ left = Math.min(x, left); ++ right = Math.max(x, right); ++ bottom = Math.max(y, bottom); ++ ++ if (!v.controls) { ++ return; ++ } ++ ++ cl = controls.left; ++ cr = controls.right; ++ ++ if (!cl || !cr) { ++ return; ++ } ++ ++ a = v._relative ? cl.x + x : cl.x; ++ b = v._relative ? cl.y + y : cl.y; ++ c = v._relative ? cr.x + x : cr.x; ++ d = v._relative ? cr.y + y : cr.y; ++ ++ if (!a || !b || !c || !d) { ++ return; ++ } ++ ++ top = Math.min(b, d, top); ++ left = Math.min(a, c, left); ++ right = Math.max(a, c, right); ++ bottom = Math.max(b, d, bottom); ++ ++ }); ++ ++ // Expand borders ++ ++ if (_.isNumber(border)) { ++ top -= border; ++ left -= border; ++ right += border; ++ bottom += border; ++ } ++ ++ width = right - left; ++ height = bottom - top; ++ ++ rect.top = top; ++ rect.left = left; ++ rect.right = right; ++ rect.bottom = bottom; ++ rect.width = width; ++ rect.height = height; ++ ++ if (!rect.centroid) { ++ rect.centroid = {}; ++ } ++ ++ rect.centroid.x = - left; ++ rect.centroid.y = - top; ++ ++ }, ++ ++ render: function(gl, program, forcedParent) { ++ ++ if (!this._visible || !this._opacity) { ++ return this; ++ } ++ ++ this._update(); ++ ++ // Calculate what changed ++ ++ var parent = this.parent; ++ var flagParentMatrix = parent._matrix.manual || parent._flagMatrix; ++ var flagMatrix = this._matrix.manual || this._flagMatrix; ++ var flagTexture = this._flagVertices || this._flagFill ++ || (this._fill instanceof Two.LinearGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagEndPoints)) ++ || (this._fill instanceof Two.RadialGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagRadius || this._fill._flagCenter || this._fill._flagFocal)) ++ || (this._fill instanceof Two.Texture && (this._fill._flagLoaded && this._fill.loaded || this._fill._flagOffset || this._fill._flagScale)) ++ || (this._stroke instanceof Two.LinearGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagEndPoints)) ++ || (this._stroke instanceof Two.RadialGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagRadius || this._stroke._flagCenter || this._stroke._flagFocal)) ++ || (this._stroke instanceof Two.Texture && (this._stroke._flagLoaded && this._stroke.loaded || this._stroke._flagOffset || this._fill._flagScale)) ++ || this._flagStroke || this._flagLinewidth || this._flagOpacity ++ || parent._flagOpacity || this._flagVisible || this._flagCap ++ || this._flagJoin || this._flagMiter || this._flagScale ++ || !this._renderer.texture; ++ ++ if (flagParentMatrix || flagMatrix) { ++ ++ if (!this._renderer.matrix) { ++ this._renderer.matrix = new Two.Array(9); ++ } ++ ++ // Reduce amount of object / array creation / deletion ++ ++ this._matrix.toArray(true, transformation); ++ ++ multiplyMatrix(transformation, parent._renderer.matrix, this._renderer.matrix); ++ this._renderer.scale = this._scale * parent._renderer.scale; ++ ++ } ++ ++ if (flagTexture) { ++ ++ if (!this._renderer.rect) { ++ this._renderer.rect = {}; ++ } ++ ++ if (!this._renderer.triangles) { ++ this._renderer.triangles = new Two.Array(12); ++ } ++ ++ this._renderer.opacity = this._opacity * parent._renderer.opacity; ++ ++ webgl.path.getBoundingClientRect(this._vertices, this._linewidth, this._renderer.rect); ++ webgl.getTriangles(this._renderer.rect, this._renderer.triangles); ++ ++ webgl.updateBuffer.call(webgl, gl, this, program); ++ webgl.updateTexture.call(webgl, gl, this); ++ ++ } ++ ++ // if (this._mask) { ++ // webgl[this._mask._renderer.type].render.call(mask, gl, program, this); ++ // } ++ ++ if (this._clip && !forcedParent) { ++ return; ++ } ++ ++ // Draw Texture ++ ++ gl.bindBuffer(gl.ARRAY_BUFFER, this._renderer.textureCoordsBuffer); ++ ++ gl.vertexAttribPointer(program.textureCoords, 2, gl.FLOAT, false, 0, 0); ++ ++ gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture); ++ ++ ++ // Draw Rect ++ ++ gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix); ++ ++ gl.bindBuffer(gl.ARRAY_BUFFER, this._renderer.buffer); ++ ++ gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0); ++ ++ gl.drawArrays(gl.TRIANGLES, 0, 6); ++ ++ return this.flagReset(); ++ ++ } ++ ++ }, ++ ++ text: { ++ ++ updateCanvas: function(elem) { ++ ++ var canvas = this.canvas; ++ var ctx = this.ctx; ++ ++ // Styles ++ var scale = elem._renderer.scale; ++ var stroke = elem._stroke; ++ var linewidth = elem._linewidth * scale; ++ var fill = elem._fill; ++ var opacity = elem._renderer.opacity || elem._opacity; ++ ++ canvas.width = Math.max(Math.ceil(elem._renderer.rect.width * scale), 1); ++ canvas.height = Math.max(Math.ceil(elem._renderer.rect.height * scale), 1); ++ ++ var centroid = elem._renderer.rect.centroid; ++ var cx = centroid.x; ++ var cy = centroid.y; ++ ++ var a, b, c, d, e, sx, sy; ++ var isOffset = fill._renderer && fill._renderer.offset ++ && stroke._renderer && stroke._renderer.offset; ++ ++ ctx.clearRect(0, 0, canvas.width, canvas.height); ++ ++ if (!isOffset) { ++ ctx.font = [elem._style, elem._weight, elem._size + 'px/' + ++ elem._leading + 'px', elem._family].join(' '); ++ } ++ ++ ctx.textAlign = 'center'; ++ ctx.textBaseline = 'middle'; ++ ++ // Styles ++ if (fill) { ++ if (_.isString(fill)) { ++ ctx.fillStyle = fill; ++ } else { ++ webgl[fill._renderer.type].render.call(fill, ctx, elem); ++ ctx.fillStyle = fill._renderer.effect; ++ } ++ } ++ if (stroke) { ++ if (_.isString(stroke)) { ++ ctx.strokeStyle = stroke; ++ } else { ++ webgl[stroke._renderer.type].render.call(stroke, ctx, elem); ++ ctx.strokeStyle = stroke._renderer.effect; ++ } ++ } ++ if (linewidth) { ++ ctx.lineWidth = linewidth; ++ } ++ if (_.isNumber(opacity)) { ++ ctx.globalAlpha = opacity; ++ } ++ ++ ctx.save(); ++ ctx.scale(scale, scale); ++ ctx.translate(cx, cy); ++ ++ if (!webgl.isHidden.test(fill)) { ++ ++ if (fill._renderer && fill._renderer.offset) { ++ ++ sx = toFixed(fill._renderer.scale.x); ++ sy = toFixed(fill._renderer.scale.y); ++ ++ ctx.save(); ++ ctx.translate( - toFixed(fill._renderer.offset.x), ++ - toFixed(fill._renderer.offset.y)); ++ ctx.scale(sx, sy); ++ ++ a = elem._size / fill._renderer.scale.y; ++ b = elem._leading / fill._renderer.scale.y; ++ ctx.font = [elem._style, elem._weight, toFixed(a) + 'px/', ++ toFixed(b) + 'px', elem._family].join(' '); ++ ++ c = fill._renderer.offset.x / fill._renderer.scale.x; ++ d = fill._renderer.offset.y / fill._renderer.scale.y; ++ ++ ctx.fillText(elem.value, toFixed(c), toFixed(d)); ++ ctx.restore(); ++ ++ } else { ++ ctx.fillText(elem.value, 0, 0); ++ } ++ ++ } ++ ++ if (!webgl.isHidden.test(stroke)) { ++ ++ if (stroke._renderer && stroke._renderer.offset) { ++ ++ sx = toFixed(stroke._renderer.scale.x); ++ sy = toFixed(stroke._renderer.scale.y); ++ ++ ctx.save(); ++ ctx.translate(- toFixed(stroke._renderer.offset.x), ++ - toFixed(stroke._renderer.offset.y)); ++ ctx.scale(sx, sy); ++ ++ a = elem._size / stroke._renderer.scale.y; ++ b = elem._leading / stroke._renderer.scale.y; ++ ctx.font = [elem._style, elem._weight, toFixed(a) + 'px/', ++ toFixed(b) + 'px', elem._family].join(' '); ++ ++ c = stroke._renderer.offset.x / stroke._renderer.scale.x; ++ d = stroke._renderer.offset.y / stroke._renderer.scale.y; ++ e = linewidth / stroke._renderer.scale.x; ++ ++ ctx.lineWidth = toFixed(e); ++ ctx.strokeText(elem.value, toFixed(c), toFixed(d)); ++ ctx.restore(); ++ ++ } else { ++ ctx.strokeText(elem.value, 0, 0); ++ } ++ ++ } ++ ++ ctx.restore(); ++ ++ }, ++ ++ getBoundingClientRect: function(elem, rect) { ++ ++ var ctx = webgl.ctx; ++ ++ ctx.font = [elem._style, elem._weight, elem._size + 'px/' + ++ elem._leading + 'px', elem._family].join(' '); ++ ++ ctx.textAlign = 'center'; ++ ctx.textBaseline = elem._baseline; ++ ++ // TODO: Estimate this better ++ var width = ctx.measureText(elem._value).width; ++ var height = Math.max(elem._size || elem._leading); ++ ++ if (this._linewidth && !webgl.isHidden.test(this._stroke)) { ++ // width += this._linewidth; // TODO: Not sure if the `measure` calcs this. ++ height += this._linewidth; ++ } ++ ++ var w = width / 2; ++ var h = height / 2; ++ ++ switch (webgl.alignments[elem._alignment] || elem._alignment) { ++ ++ case webgl.alignments.left: ++ rect.left = 0; ++ rect.right = width; ++ break; ++ case webgl.alignments.right: ++ rect.left = - width; ++ rect.right = 0; ++ break; ++ default: ++ rect.left = - w; ++ rect.right = w; ++ } ++ ++ // TODO: Gradients aren't inherited... ++ switch (elem._baseline) { ++ case 'bottom': ++ rect.top = - height; ++ rect.bottom = 0; ++ break; ++ case 'top': ++ rect.top = 0; ++ rect.bottom = height; ++ break; ++ default: ++ rect.top = - h; ++ rect.bottom = h; ++ } ++ ++ rect.width = width; ++ rect.height = height; ++ ++ if (!rect.centroid) { ++ rect.centroid = {}; ++ } ++ ++ // TODO: ++ rect.centroid.x = w; ++ rect.centroid.y = h; ++ ++ }, ++ ++ render: function(gl, program, forcedParent) { ++ ++ if (!this._visible || !this._opacity) { ++ return this; ++ } ++ ++ this._update(); ++ ++ // Calculate what changed ++ ++ var parent = this.parent; ++ var flagParentMatrix = parent._matrix.manual || parent._flagMatrix; ++ var flagMatrix = this._matrix.manual || this._flagMatrix; ++ var flagTexture = this._flagVertices || this._flagFill ++ || (this._fill instanceof Two.LinearGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagEndPoints)) ++ || (this._fill instanceof Two.RadialGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagRadius || this._fill._flagCenter || this._fill._flagFocal)) ++ || (this._fill instanceof Two.Texture && (this._fill._flagLoaded && this._fill.loaded)) ++ || (this._stroke instanceof Two.LinearGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagEndPoints)) ++ || (this._stroke instanceof Two.RadialGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagRadius || this._stroke._flagCenter || this._stroke._flagFocal)) ++ || (this._texture instanceof Two.Texture && (this._texture._flagLoaded && this._texture.loaded)) ++ || this._flagStroke || this._flagLinewidth || this._flagOpacity ++ || parent._flagOpacity || this._flagVisible || this._flagScale ++ || this._flagValue || this._flagFamily || this._flagSize ++ || this._flagLeading || this._flagAlignment || this._flagBaseline ++ || this._flagStyle || this._flagWeight || this._flagDecoration ++ || !this._renderer.texture; ++ ++ if (flagParentMatrix || flagMatrix) { ++ ++ if (!this._renderer.matrix) { ++ this._renderer.matrix = new Two.Array(9); ++ } ++ ++ // Reduce amount of object / array creation / deletion ++ ++ this._matrix.toArray(true, transformation); ++ ++ multiplyMatrix(transformation, parent._renderer.matrix, this._renderer.matrix); ++ this._renderer.scale = this._scale * parent._renderer.scale; ++ ++ } ++ ++ if (flagTexture) { ++ ++ if (!this._renderer.rect) { ++ this._renderer.rect = {}; ++ } ++ ++ if (!this._renderer.triangles) { ++ this._renderer.triangles = new Two.Array(12); ++ } ++ ++ this._renderer.opacity = this._opacity * parent._renderer.opacity; ++ ++ webgl.text.getBoundingClientRect(this, this._renderer.rect); ++ webgl.getTriangles(this._renderer.rect, this._renderer.triangles); ++ ++ webgl.updateBuffer.call(webgl, gl, this, program); ++ webgl.updateTexture.call(webgl, gl, this); ++ ++ } ++ ++ // if (this._mask) { ++ // webgl[this._mask._renderer.type].render.call(mask, gl, program, this); ++ // } ++ ++ if (this._clip && !forcedParent) { ++ return; ++ } ++ ++ // Draw Texture ++ ++ gl.bindBuffer(gl.ARRAY_BUFFER, this._renderer.textureCoordsBuffer); ++ ++ gl.vertexAttribPointer(program.textureCoords, 2, gl.FLOAT, false, 0, 0); ++ ++ gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture); ++ ++ ++ // Draw Rect ++ ++ gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix); ++ ++ gl.bindBuffer(gl.ARRAY_BUFFER, this._renderer.buffer); ++ ++ gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0); ++ ++ gl.drawArrays(gl.TRIANGLES, 0, 6); ++ ++ return this.flagReset(); ++ ++ } ++ ++ }, ++ ++ 'linear-gradient': { ++ ++ render: function(ctx, elem) { ++ ++ if (!ctx.canvas.getContext('2d')) { ++ return; ++ } ++ ++ this._update(); ++ ++ if (!this._renderer.effect || this._flagEndPoints || this._flagStops) { ++ ++ this._renderer.effect = ctx.createLinearGradient( ++ this.left._x, this.left._y, ++ this.right._x, this.right._y ++ ); ++ ++ for (var i = 0; i < this.stops.length; i++) { ++ var stop = this.stops[i]; ++ this._renderer.effect.addColorStop(stop._offset, stop._color); ++ } ++ ++ } ++ ++ return this.flagReset(); ++ ++ } ++ ++ }, ++ ++ 'radial-gradient': { ++ ++ render: function(ctx, elem) { ++ ++ if (!ctx.canvas.getContext('2d')) { ++ return; ++ } ++ ++ this._update(); ++ ++ if (!this._renderer.effect || this._flagCenter || this._flagFocal ++ || this._flagRadius || this._flagStops) { ++ ++ this._renderer.effect = ctx.createRadialGradient( ++ this.center._x, this.center._y, 0, ++ this.focal._x, this.focal._y, this._radius ++ ); ++ ++ for (var i = 0; i < this.stops.length; i++) { ++ var stop = this.stops[i]; ++ this._renderer.effect.addColorStop(stop._offset, stop._color); ++ } ++ ++ } ++ ++ return this.flagReset(); ++ ++ } ++ ++ }, ++ ++ texture: { ++ ++ render: function(ctx, elem) { ++ ++ if (!ctx.canvas.getContext('2d')) { ++ return; ++ } ++ ++ this._update(); ++ ++ var image = this.image; ++ var repeat; ++ ++ if (!this._renderer.effect || ((this._flagLoaded || this._flagRepeat) && this.loaded)) { ++ this._renderer.effect = ctx.createPattern(image, this._repeat); ++ } ++ ++ if (this._flagOffset || this._flagLoaded || this._flagScale) { ++ ++ if (!(this._renderer.offset instanceof Two.Vector)) { ++ this._renderer.offset = new Two.Vector(); ++ } ++ ++ this._renderer.offset.x = this._offset.x; ++ this._renderer.offset.y = this._offset.y; ++ ++ if (image) { ++ ++ this._renderer.offset.x -= image.width / 2; ++ this._renderer.offset.y += image.height / 2; ++ ++ if (this._scale instanceof Two.Vector) { ++ this._renderer.offset.x *= this._scale.x; ++ this._renderer.offset.y *= this._scale.y; ++ } else { ++ this._renderer.offset.x *= this._scale; ++ this._renderer.offset.y *= this._scale; ++ } ++ } ++ ++ } ++ ++ if (this._flagScale || this._flagLoaded) { ++ ++ if (!(this._renderer.scale instanceof Two.Vector)) { ++ this._renderer.scale = new Two.Vector(); ++ } ++ ++ if (this._scale instanceof Two.Vector) { ++ this._renderer.scale.copy(this._scale); ++ } else { ++ this._renderer.scale.set(this._scale, this._scale); ++ } ++ ++ } ++ ++ return this.flagReset(); ++ ++ } ++ ++ }, ++ ++ getTriangles: function(rect, triangles) { ++ ++ var top = rect.top, ++ left = rect.left, ++ right = rect.right, ++ bottom = rect.bottom; ++ ++ // First Triangle ++ ++ triangles[0] = left; ++ triangles[1] = top; ++ ++ triangles[2] = right; ++ triangles[3] = top; ++ ++ triangles[4] = left; ++ triangles[5] = bottom; ++ ++ // Second Triangle ++ ++ triangles[6] = left; ++ triangles[7] = bottom; ++ ++ triangles[8] = right; ++ triangles[9] = top; ++ ++ triangles[10] = right; ++ triangles[11] = bottom; ++ ++ }, ++ ++ updateTexture: function(gl, elem) { ++ ++ this[elem._renderer.type].updateCanvas.call(webgl, elem); ++ ++ if (elem._renderer.texture) { ++ gl.deleteTexture(elem._renderer.texture); ++ } ++ ++ gl.bindBuffer(gl.ARRAY_BUFFER, elem._renderer.textureCoordsBuffer); ++ ++ // TODO: Is this necessary every time or can we do once? ++ // TODO: Create a registry for textures ++ elem._renderer.texture = gl.createTexture(); ++ gl.bindTexture(gl.TEXTURE_2D, elem._renderer.texture); ++ ++ // Set the parameters so we can render any size image. ++ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); ++ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); ++ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); ++ // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); ++ // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); ++ // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); ++ ++ if (this.canvas.width <= 0 || this.canvas.height <= 0) { ++ return; ++ } ++ ++ // Upload the image into the texture. ++ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.canvas); ++ ++ }, ++ ++ updateBuffer: function(gl, elem, program) { ++ ++ if (_.isObject(elem._renderer.buffer)) { ++ gl.deleteBuffer(elem._renderer.buffer); ++ } ++ ++ elem._renderer.buffer = gl.createBuffer(); ++ ++ gl.bindBuffer(gl.ARRAY_BUFFER, elem._renderer.buffer); ++ gl.enableVertexAttribArray(program.position); ++ ++ gl.bufferData(gl.ARRAY_BUFFER, elem._renderer.triangles, gl.STATIC_DRAW); ++ ++ if (_.isObject(elem._renderer.textureCoordsBuffer)) { ++ gl.deleteBuffer(elem._renderer.textureCoordsBuffer); ++ } ++ ++ elem._renderer.textureCoordsBuffer = gl.createBuffer(); ++ ++ gl.bindBuffer(gl.ARRAY_BUFFER, elem._renderer.textureCoordsBuffer); ++ gl.enableVertexAttribArray(program.textureCoords); ++ ++ gl.bufferData(gl.ARRAY_BUFFER, this.uv, gl.STATIC_DRAW); ++ ++ }, ++ ++ program: { ++ ++ create: function(gl, shaders) { ++ var program, linked, error; ++ program = gl.createProgram(); ++ _.each(shaders, function(s) { ++ gl.attachShader(program, s); ++ }); ++ ++ gl.linkProgram(program); ++ linked = gl.getProgramParameter(program, gl.LINK_STATUS); ++ if (!linked) { ++ error = gl.getProgramInfoLog(program); ++ gl.deleteProgram(program); ++ throw new Two.Utils.Error('unable to link program: ' + error); ++ } ++ ++ return program; ++ ++ } ++ ++ }, ++ ++ shaders: { ++ ++ create: function(gl, source, type) { ++ var shader, compiled, error; ++ shader = gl.createShader(gl[type]); ++ gl.shaderSource(shader, source); ++ gl.compileShader(shader); ++ ++ compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); ++ if (!compiled) { ++ error = gl.getShaderInfoLog(shader); ++ gl.deleteShader(shader); ++ throw new Two.Utils.Error('unable to compile shader ' + shader + ': ' + error); ++ } ++ ++ return shader; ++ ++ }, ++ ++ types: { ++ vertex: 'VERTEX_SHADER', ++ fragment: 'FRAGMENT_SHADER' ++ }, ++ ++ vertex: [ ++ 'attribute vec2 a_position;', ++ 'attribute vec2 a_textureCoords;', ++ '', ++ 'uniform mat3 u_matrix;', ++ 'uniform vec2 u_resolution;', ++ '', ++ 'varying vec2 v_textureCoords;', ++ '', ++ 'void main() {', ++ ' vec2 projected = (u_matrix * vec3(a_position, 1.0)).xy;', ++ ' vec2 normal = projected / u_resolution;', ++ ' vec2 clipspace = (normal * 2.0) - 1.0;', ++ '', ++ ' gl_Position = vec4(clipspace * vec2(1.0, -1.0), 0.0, 1.0);', ++ ' v_textureCoords = a_textureCoords;', ++ '}' ++ ].join('\n'), ++ ++ fragment: [ ++ 'precision mediump float;', ++ '', ++ 'uniform sampler2D u_image;', ++ 'varying vec2 v_textureCoords;', ++ '', ++ 'void main() {', ++ ' gl_FragColor = texture2D(u_image, v_textureCoords);', ++ '}' ++ ].join('\n') ++ ++ }, ++ ++ TextureRegistry: new Two.Registry() ++ ++ }; ++ ++ webgl.ctx = webgl.canvas.getContext('2d'); ++ ++ var Renderer = Two[Two.Types.webgl] = function(options) { ++ ++ var params, gl, vs, fs; ++ this.domElement = options.domElement || document.createElement('canvas'); ++ ++ // Everything drawn on the canvas needs to come from the stage. ++ this.scene = new Two.Group(); ++ this.scene.parent = this; ++ ++ this._renderer = { ++ matrix: new Two.Array(identity), ++ scale: 1, ++ opacity: 1 ++ }; ++ this._flagMatrix = true; ++ ++ // http://games.greggman.com/game/webgl-and-alpha/ ++ // http://www.khronos.org/registry/webgl/specs/latest/#5.2 ++ params = _.defaults(options || {}, { ++ antialias: false, ++ alpha: true, ++ premultipliedAlpha: true, ++ stencil: true, ++ preserveDrawingBuffer: true, ++ overdraw: false ++ }); ++ ++ this.overdraw = params.overdraw; ++ ++ gl = this.ctx = this.domElement.getContext('webgl', params) || ++ this.domElement.getContext('experimental-webgl', params); ++ ++ if (!this.ctx) { ++ throw new Two.Utils.Error( ++ 'unable to create a webgl context. Try using another renderer.'); ++ } ++ ++ // Compile Base Shaders to draw in pixel space. ++ vs = webgl.shaders.create( ++ gl, webgl.shaders.vertex, webgl.shaders.types.vertex); ++ fs = webgl.shaders.create( ++ gl, webgl.shaders.fragment, webgl.shaders.types.fragment); ++ ++ this.program = webgl.program.create(gl, [vs, fs]); ++ gl.useProgram(this.program); ++ ++ // Create and bind the drawing buffer ++ ++ // look up where the vertex data needs to go. ++ this.program.position = gl.getAttribLocation(this.program, 'a_position'); ++ this.program.matrix = gl.getUniformLocation(this.program, 'u_matrix'); ++ this.program.textureCoords = gl.getAttribLocation(this.program, 'a_textureCoords'); ++ ++ // Copied from Three.js WebGLRenderer ++ gl.disable(gl.DEPTH_TEST); ++ ++ // Setup some initial statements of the gl context ++ gl.enable(gl.BLEND); ++ ++ // https://code.google.com/p/chromium/issues/detail?id=316393 ++ // gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, gl.TRUE); ++ ++ gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD); ++ gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, ++ gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); ++ ++ }; ++ ++ _.extend(Renderer, { ++ ++ Utils: webgl ++ ++ }); ++ ++ _.extend(Renderer.prototype, Two.Utils.Events, { ++ ++ setSize: function(width, height, ratio) { ++ ++ this.width = width; ++ this.height = height; ++ ++ this.ratio = _.isUndefined(ratio) ? getRatio(this.ctx) : ratio; ++ ++ this.domElement.width = width * this.ratio; ++ this.domElement.height = height * this.ratio; ++ ++ _.extend(this.domElement.style, { ++ width: width + 'px', ++ height: height + 'px' ++ }); ++ ++ width *= this.ratio; ++ height *= this.ratio; ++ ++ // Set for this.stage parent scaling to account for HDPI ++ this._renderer.matrix[0] = this._renderer.matrix[4] = this._renderer.scale = this.ratio; ++ ++ this._flagMatrix = true; ++ ++ this.ctx.viewport(0, 0, width, height); ++ ++ var resolutionLocation = this.ctx.getUniformLocation( ++ this.program, 'u_resolution'); ++ this.ctx.uniform2f(resolutionLocation, width, height); ++ ++ return this; ++ ++ }, ++ ++ render: function() { ++ ++ var gl = this.ctx; ++ ++ if (!this.overdraw) { ++ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); ++ } ++ ++ webgl.group.render.call(this.scene, gl, this.program); ++ this._flagMatrix = false; ++ ++ return this; ++ ++ } ++ ++ }); ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ var _ = Two.Utils; ++ ++ var Shape = Two.Shape = function() { ++ ++ // Private object for renderer specific variables. ++ this._renderer = {}; ++ this._renderer.flagMatrix = _.bind(Shape.FlagMatrix, this); ++ this.isShape = true; ++ ++ this.id = Two.Identifier + Two.uniqueId(); ++ this.classList = []; ++ ++ // Define matrix properties which all inherited ++ // objects of Shape have. ++ ++ this._matrix = new Two.Matrix(); ++ ++ this.translation = new Two.Vector(); ++ this.rotation = 0; ++ this.scale = 1; ++ ++ }; ++ ++ _.extend(Shape, { ++ ++ FlagMatrix: function() { ++ this._flagMatrix = true; ++ }, ++ ++ MakeObservable: function(object) { ++ ++ Object.defineProperty(object, 'translation', { ++ enumerable: true, ++ get: function() { ++ return this._translation; ++ }, ++ set: function(v) { ++ if (this._translation) { ++ this._translation.unbind(Two.Events.change, this._renderer.flagMatrix); ++ } ++ this._translation = v; ++ this._translation.bind(Two.Events.change, this._renderer.flagMatrix); ++ Shape.FlagMatrix.call(this); ++ } ++ }); ++ ++ Object.defineProperty(object, 'rotation', { ++ enumerable: true, ++ get: function() { ++ return this._rotation; ++ }, ++ set: function(v) { ++ this._rotation = v; ++ this._flagMatrix = true; ++ } ++ }); ++ ++ Object.defineProperty(object, 'scale', { ++ enumerable: true, ++ get: function() { ++ return this._scale; ++ }, ++ set: function(v) { ++ ++ if (this._scale instanceof Two.Vector) { ++ this._scale.unbind(Two.Events.change, this._renderer.flagMatrix); ++ } ++ ++ this._scale = v; ++ ++ if (this._scale instanceof Two.Vector) { ++ this._scale.bind(Two.Events.change, this._renderer.flagMatrix); ++ } ++ ++ this._flagMatrix = true; ++ this._flagScale = true; ++ ++ } ++ }); ++ ++ } ++ ++ }); ++ ++ _.extend(Shape.prototype, Two.Utils.Events, { ++ ++ // Flags ++ ++ _flagMatrix: true, ++ _flagScale: false, ++ ++ // _flagMask: false, ++ // _flagClip: false, ++ ++ // Underlying Properties ++ ++ _rotation: 0, ++ _scale: 1, ++ _translation: null, ++ ++ // _mask: null, ++ // _clip: false, ++ ++ addTo: function(group) { ++ group.add(this); ++ return this; ++ }, ++ ++ clone: function() { ++ var clone = new Shape(); ++ clone.translation.copy(this.translation); ++ clone.rotation = this.rotation; ++ clone.scale = this.scale; ++ _.each(Shape.Properties, function(k) { ++ clone[k] = this[k]; ++ }, this); ++ return clone._update(); ++ }, ++ ++ /** ++ * To be called before render that calculates and collates all information ++ * to be as up-to-date as possible for the render. Called once a frame. ++ */ ++ _update: function(deep) { ++ ++ if (!this._matrix.manual && this._flagMatrix) { ++ ++ this._matrix ++ .identity() ++ .translate(this.translation.x, this.translation.y); ++ ++ if (this._scale instanceof Two.Vector) { ++ this._matrix.scale(this._scale.x, this._scale.y); ++ } else { ++ this._matrix.scale(this._scale); ++ } ++ ++ this._matrix.rotate(this.rotation); ++ ++ } ++ ++ if (deep) { ++ // Bubble up to parents mainly for `getBoundingClientRect` method. ++ if (this.parent && this.parent._update) { ++ this.parent._update(); ++ } ++ } ++ ++ return this; ++ ++ }, ++ ++ flagReset: function() { ++ ++ this._flagMatrix = this._flagScale = false; ++ ++ return this; ++ ++ } ++ ++ }); ++ ++ Shape.MakeObservable(Shape.prototype); ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ /** ++ * Constants ++ */ ++ ++ var min = Math.min, max = Math.max, round = Math.round, ++ getComputedMatrix = Two.Utils.getComputedMatrix; ++ ++ var commands = {}; ++ var _ = Two.Utils; ++ ++ _.each(Two.Commands, function(v, k) { ++ commands[k] = new RegExp(v); ++ }); ++ ++ var Path = Two.Path = function(vertices, closed, curved, manual) { ++ ++ Two.Shape.call(this); ++ ++ this._renderer.type = 'path'; ++ this._renderer.flagVertices = _.bind(Path.FlagVertices, this); ++ this._renderer.bindVertices = _.bind(Path.BindVertices, this); ++ this._renderer.unbindVertices = _.bind(Path.UnbindVertices, this); ++ ++ this._renderer.flagFill = _.bind(Path.FlagFill, this); ++ this._renderer.flagStroke = _.bind(Path.FlagStroke, this); ++ ++ this._closed = !!closed; ++ this._curved = !!curved; ++ ++ this.beginning = 0; ++ this.ending = 1; ++ ++ // Style properties ++ ++ this.fill = '#fff'; ++ this.stroke = '#000'; ++ this.linewidth = 1.0; ++ this.opacity = 1.0; ++ this.visible = true; ++ ++ this.cap = 'butt'; // Default of Adobe Illustrator ++ this.join = 'miter'; // Default of Adobe Illustrator ++ this.miter = 4; // Default of Adobe Illustrator ++ ++ this._vertices = []; ++ this.vertices = vertices; ++ ++ // Determines whether or not two.js should calculate curves, lines, and ++ // commands automatically for you or to let the developer manipulate them ++ // for themselves. ++ this.automatic = !manual; ++ ++ }; ++ ++ _.extend(Path, { ++ ++ Properties: [ ++ 'fill', ++ 'stroke', ++ 'linewidth', ++ 'opacity', ++ 'visible', ++ 'cap', ++ 'join', ++ 'miter', ++ ++ 'closed', ++ 'curved', ++ 'automatic', ++ 'beginning', ++ 'ending' ++ ], ++ ++ FlagVertices: function() { ++ this._flagVertices = true; ++ this._flagLength = true; ++ }, ++ ++ BindVertices: function(items) { ++ ++ // This function is called a lot ++ // when importing a large SVG ++ var i = items.length; ++ while (i--) { ++ items[i].bind(Two.Events.change, this._renderer.flagVertices); ++ } ++ ++ this._renderer.flagVertices(); ++ ++ }, ++ ++ UnbindVertices: function(items) { ++ ++ var i = items.length; ++ while (i--) { ++ items[i].unbind(Two.Events.change, this._renderer.flagVertices); ++ } ++ ++ this._renderer.flagVertices(); ++ ++ }, ++ ++ FlagFill: function() { ++ this._flagFill = true; ++ }, ++ ++ FlagStroke: function() { ++ this._flagStroke = true; ++ }, ++ ++ MakeObservable: function(object) { ++ ++ Two.Shape.MakeObservable(object); ++ ++ // Only the 6 defined properties are flagged like this. The subsequent ++ // properties behave differently and need to be hand written. ++ _.each(Path.Properties.slice(2, 8), Two.Utils.defineProperty, object); ++ ++ Object.defineProperty(object, 'fill', { ++ enumerable: true, ++ get: function() { ++ return this._fill; ++ }, ++ set: function(f) { ++ ++ if (this._fill instanceof Two.Gradient ++ || this._fill instanceof Two.LinearGradient ++ || this._fill instanceof Two.RadialGradient ++ || this._fill instanceof Two.Texture) { ++ this._fill.unbind(Two.Events.change, this._renderer.flagFill); ++ } ++ ++ this._fill = f; ++ this._flagFill = true; ++ ++ if (this._fill instanceof Two.Gradient ++ || this._fill instanceof Two.LinearGradient ++ || this._fill instanceof Two.RadialGradient ++ || this._fill instanceof Two.Texture) { ++ this._fill.bind(Two.Events.change, this._renderer.flagFill); ++ } ++ ++ } ++ }); ++ ++ Object.defineProperty(object, 'stroke', { ++ enumerable: true, ++ get: function() { ++ return this._stroke; ++ }, ++ set: function(f) { ++ ++ if (this._stroke instanceof Two.Gradient ++ || this._stroke instanceof Two.LinearGradient ++ || this._stroke instanceof Two.RadialGradient ++ || this._stroke instanceof Two.Texture) { ++ this._stroke.unbind(Two.Events.change, this._renderer.flagStroke); ++ } ++ ++ this._stroke = f; ++ this._flagStroke = true; ++ ++ if (this._stroke instanceof Two.Gradient ++ || this._stroke instanceof Two.LinearGradient ++ || this._stroke instanceof Two.RadialGradient ++ || this._stroke instanceof Two.Texture) { ++ this._stroke.bind(Two.Events.change, this._renderer.flagStroke); ++ } ++ ++ } ++ }); ++ ++ Object.defineProperty(object, 'length', { ++ get: function() { ++ if (this._flagLength) { ++ this._updateLength(); ++ } ++ return this._length; ++ } ++ }); ++ ++ Object.defineProperty(object, 'closed', { ++ enumerable: true, ++ get: function() { ++ return this._closed; ++ }, ++ set: function(v) { ++ this._closed = !!v; ++ this._flagVertices = true; ++ } ++ }); ++ ++ Object.defineProperty(object, 'curved', { ++ enumerable: true, ++ get: function() { ++ return this._curved; ++ }, ++ set: function(v) { ++ this._curved = !!v; ++ this._flagVertices = true; ++ } ++ }); ++ ++ Object.defineProperty(object, 'automatic', { ++ enumerable: true, ++ get: function() { ++ return this._automatic; ++ }, ++ set: function(v) { ++ if (v === this._automatic) { ++ return; ++ } ++ this._automatic = !!v; ++ var method = this._automatic ? 'ignore' : 'listen'; ++ _.each(this.vertices, function(v) { ++ v[method](); ++ }); ++ } ++ }); ++ ++ Object.defineProperty(object, 'beginning', { ++ enumerable: true, ++ get: function() { ++ return this._beginning; ++ }, ++ set: function(v) { ++ this._beginning = v; ++ this._flagVertices = true; ++ } ++ }); ++ ++ Object.defineProperty(object, 'ending', { ++ enumerable: true, ++ get: function() { ++ return this._ending; ++ }, ++ set: function(v) { ++ this._ending = v; ++ this._flagVertices = true; ++ } ++ }); ++ ++ Object.defineProperty(object, 'vertices', { ++ ++ enumerable: true, ++ ++ get: function() { ++ return this._collection; ++ }, ++ ++ set: function(vertices) { ++ ++ var updateVertices = this._renderer.flagVertices; ++ var bindVertices = this._renderer.bindVertices; ++ var unbindVertices = this._renderer.unbindVertices; ++ ++ // Remove previous listeners ++ if (this._collection) { ++ this._collection ++ .unbind(Two.Events.insert, bindVertices) ++ .unbind(Two.Events.remove, unbindVertices); ++ } ++ ++ // Create new Collection with copy of vertices ++ this._collection = new Two.Utils.Collection((vertices || []).slice(0)); ++ ++ // Listen for Collection changes and bind / unbind ++ this._collection ++ .bind(Two.Events.insert, bindVertices) ++ .bind(Two.Events.remove, unbindVertices); ++ ++ // Bind Initial Vertices ++ bindVertices(this._collection); ++ ++ } ++ ++ }); ++ ++ Object.defineProperty(object, 'clip', { ++ enumerable: true, ++ get: function() { ++ return this._clip; ++ }, ++ set: function(v) { ++ this._clip = v; ++ this._flagClip = true; ++ } ++ }); ++ ++ } ++ ++ }); ++ ++ _.extend(Path.prototype, Two.Shape.prototype, { ++ ++ // Flags ++ // http://en.wikipedia.org/wiki/Flag ++ ++ _flagVertices: true, ++ _flagLength: true, ++ ++ _flagFill: true, ++ _flagStroke: true, ++ _flagLinewidth: true, ++ _flagOpacity: true, ++ _flagVisible: true, ++ ++ _flagCap: true, ++ _flagJoin: true, ++ _flagMiter: true, ++ ++ _flagClip: false, ++ ++ // Underlying Properties ++ ++ _length: 0, ++ ++ _fill: '#fff', ++ _stroke: '#000', ++ _linewidth: 1.0, ++ _opacity: 1.0, ++ _visible: true, ++ ++ _cap: 'round', ++ _join: 'round', ++ _miter: 4, ++ ++ _closed: true, ++ _curved: false, ++ _automatic: true, ++ _beginning: 0, ++ _ending: 1.0, ++ ++ _clip: false, ++ ++ clone: function(parent) { ++ ++ parent = parent || this.parent; ++ ++ var points = _.map(this.vertices, function(v) { ++ return v.clone(); ++ }); ++ ++ var clone = new Path(points, this.closed, this.curved, !this.automatic); ++ ++ _.each(Two.Path.Properties, function(k) { ++ clone[k] = this[k]; ++ }, this); ++ ++ clone.translation.copy(this.translation); ++ clone.rotation = this.rotation; ++ clone.scale = this.scale; ++ ++ if (parent) { ++ parent.add(clone); ++ } ++ ++ return clone; ++ ++ }, ++ ++ toObject: function() { ++ ++ var result = { ++ vertices: _.map(this.vertices, function(v) { ++ return v.toObject(); ++ }) ++ }; ++ ++ _.each(Two.Shape.Properties, function(k) { ++ result[k] = this[k]; ++ }, this); ++ ++ result.translation = this.translation.toObject; ++ result.rotation = this.rotation; ++ result.scale = this.scale; ++ ++ return result; ++ ++ }, ++ ++ noFill: function() { ++ this.fill = 'transparent'; ++ return this; ++ }, ++ ++ noStroke: function() { ++ this.stroke = 'transparent'; ++ return this; ++ }, ++ ++ /** ++ * Orient the vertices of the shape to the upper lefthand ++ * corner of the path. ++ */ ++ corner: function() { ++ ++ var rect = this.getBoundingClientRect(true); ++ ++ rect.centroid = { ++ x: rect.left + rect.width / 2, ++ y: rect.top + rect.height / 2 ++ }; ++ ++ _.each(this.vertices, function(v) { ++ v.addSelf(rect.centroid); ++ }); ++ ++ return this; ++ ++ }, ++ ++ /** ++ * Orient the vertices of the shape to the center of the ++ * path. ++ */ ++ center: function() { ++ ++ var rect = this.getBoundingClientRect(true); ++ ++ rect.centroid = { ++ x: rect.left + rect.width / 2, ++ y: rect.top + rect.height / 2 ++ }; ++ ++ _.each(this.vertices, function(v) { ++ v.subSelf(rect.centroid); ++ }); ++ ++ // this.translation.addSelf(rect.centroid); ++ ++ return this; ++ ++ }, ++ ++ /** ++ * Remove self from the scene / parent. ++ */ ++ remove: function() { ++ ++ if (!this.parent) { ++ return this; ++ } ++ ++ this.parent.remove(this); ++ ++ return this; ++ ++ }, ++ ++ /** ++ * Return an object with top, left, right, bottom, width, and height ++ * parameters of the group. ++ */ ++ getBoundingClientRect: function(shallow) { ++ var matrix, border, l, x, y, i, v; ++ ++ var left = Infinity, right = -Infinity, ++ top = Infinity, bottom = -Infinity; ++ ++ // TODO: Update this to not __always__ update. Just when it needs to. ++ this._update(true); ++ ++ matrix = !!shallow ? this._matrix : getComputedMatrix(this); ++ ++ border = this.linewidth / 2; ++ l = this._vertices.length; ++ ++ if (l <= 0) { ++ v = matrix.multiply(0, 0, 1); ++ return { ++ top: v.y, ++ left: v.x, ++ right: v.x, ++ bottom: v.y, ++ width: 0, ++ height: 0 ++ }; ++ } ++ ++ for (i = 0; i < l; i++) { ++ v = this._vertices[i]; ++ ++ x = v.x; ++ y = v.y; ++ ++ v = matrix.multiply(x, y, 1); ++ top = min(v.y - border, top); ++ left = min(v.x - border, left); ++ right = max(v.x + border, right); ++ bottom = max(v.y + border, bottom); ++ } ++ ++ return { ++ top: top, ++ left: left, ++ right: right, ++ bottom: bottom, ++ width: right - left, ++ height: bottom - top ++ }; ++ ++ }, ++ ++ /** ++ * Given a float `t` from 0 to 1, return a point or assign a passed `obj`'s ++ * coordinates to that percentage on this Two.Path's curve. ++ */ ++ getPointAt: function(t, obj) { ++ var ia, ib; ++ var x, x1, x2, x3, x4, y, y1, y2, y3, y4, left, right; ++ var target = this.length * Math.min(Math.max(t, 0), 1); ++ var length = this.vertices.length; ++ var last = length - 1; ++ ++ var a = null; ++ var b = null; ++ ++ for (var i = 0, l = this._lengths.length, sum = 0; i < l; i++) { ++ ++ if (sum + this._lengths[i] >= target) { ++ ++ if (this._closed) { ++ ia = Two.Utils.mod(i, length); ++ ib = Two.Utils.mod(i - 1, length); ++ if (i === 0) { ++ ia = ib; ++ ib = i; ++ } ++ } else { ++ ia = i; ++ ib = Math.min(Math.max(i - 1, 0), last); ++ } ++ ++ a = this.vertices[ia]; ++ b = this.vertices[ib]; ++ target -= sum; ++ if (this._lengths[i] !== 0) { ++ t = target / this._lengths[i]; ++ } ++ ++ break; ++ ++ } ++ ++ sum += this._lengths[i]; ++ ++ } ++ ++ // console.log(sum, a.command, b.command); ++ ++ if (_.isNull(a) || _.isNull(b)) { ++ return null; ++ } ++ ++ right = b.controls && b.controls.right; ++ left = a.controls && a.controls.left; ++ ++ x1 = b.x; ++ y1 = b.y; ++ x2 = (right || b).x; ++ y2 = (right || b).y; ++ x3 = (left || a).x; ++ y3 = (left || a).y; ++ x4 = a.x; ++ y4 = a.y; ++ ++ if (right && b._relative) { ++ x2 += b.x; ++ y2 += b.y; ++ } ++ ++ if (left && a._relative) { ++ x3 += a.x; ++ y3 += a.y; ++ } ++ ++ x = Two.Utils.getPointOnCubicBezier(t, x1, x2, x3, x4); ++ y = Two.Utils.getPointOnCubicBezier(t, y1, y2, y3, y4); ++ ++ if (_.isObject(obj)) { ++ obj.x = x; ++ obj.y = y; ++ return obj; ++ } ++ ++ return new Two.Vector(x, y); ++ ++ }, ++ ++ /** ++ * Based on closed / curved and sorting of vertices plot where all points ++ * should be and where the respective handles should be too. ++ */ ++ plot: function() { ++ ++ if (this.curved) { ++ Two.Utils.getCurveFromPoints(this._vertices, this.closed); ++ return this; ++ } ++ ++ for (var i = 0; i < this._vertices.length; i++) { ++ this._vertices[i]._command = i === 0 ? Two.Commands.move : Two.Commands.line; ++ } ++ ++ return this; ++ ++ }, ++ ++ subdivide: function(limit) { ++ //TODO: DRYness (function below) ++ this._update(); ++ ++ var last = this.vertices.length - 1; ++ var b = this.vertices[last]; ++ var closed = this._closed || this.vertices[last]._command === Two.Commands.close; ++ var points = []; ++ _.each(this.vertices, function(a, i) { ++ ++ if (i <= 0 && !closed) { ++ b = a; ++ return; ++ } ++ ++ if (a.command === Two.Commands.move) { ++ points.push(new Two.Anchor(b.x, b.y)); ++ if (i > 0) { ++ points[points.length - 1].command = Two.Commands.line; ++ } ++ b = a; ++ return; ++ } ++ ++ var verts = getSubdivisions(a, b, limit); ++ points = points.concat(verts); ++ ++ // Assign commands to all the verts ++ _.each(verts, function(v, i) { ++ if (i <= 0 && b.command === Two.Commands.move) { ++ v.command = Two.Commands.move; ++ } else { ++ v.command = Two.Commands.line; ++ } ++ }); ++ ++ if (i >= last) { ++ ++ // TODO: Add check if the two vectors in question are the same values. ++ if (this._closed && this._automatic) { ++ ++ b = a; ++ ++ verts = getSubdivisions(a, b, limit); ++ points = points.concat(verts); ++ ++ // Assign commands to all the verts ++ _.each(verts, function(v, i) { ++ if (i <= 0 && b.command === Two.Commands.move) { ++ v.command = Two.Commands.move; ++ } else { ++ v.command = Two.Commands.line; ++ } ++ }); ++ ++ } else if (closed) { ++ points.push(new Two.Anchor(a.x, a.y)); ++ } ++ ++ points[points.length - 1].command = closed ? Two.Commands.close : Two.Commands.line; ++ ++ } ++ ++ b = a; ++ ++ }, this); ++ ++ this._automatic = false; ++ this._curved = false; ++ this.vertices = points; ++ ++ return this; ++ ++ }, ++ ++ _updateLength: function(limit) { ++ //TODO: DRYness (function above) ++ this._update(); ++ ++ var length = this.vertices.length; ++ var last = length - 1; ++ var b = this.vertices[last]; ++ var closed = this._closed || this.vertices[last]._command === Two.Commands.close; ++ var sum = 0; ++ ++ if (_.isUndefined(this._lengths)) { ++ this._lengths = []; ++ } ++ ++ _.each(this.vertices, function(a, i) { ++ ++ if ((i <= 0 && !closed) || a.command === Two.Commands.move) { ++ b = a; ++ this._lengths[i] = 0; ++ return; ++ } ++ ++ this._lengths[i] = getCurveLength(a, b, limit); ++ sum += this._lengths[i]; ++ ++ if (i >= last && closed) { ++ ++ b = this.vertices[(i + 1) % length]; ++ ++ this._lengths[i + 1] = getCurveLength(a, b, limit); ++ sum += this._lengths[i + 1]; ++ ++ } ++ ++ b = a; ++ ++ }, this); ++ ++ this._length = sum; ++ ++ return this; ++ ++ }, ++ ++ _update: function() { ++ ++ if (this._flagVertices) { ++ ++ var l = this.vertices.length; ++ var last = l - 1, v; ++ ++ // TODO: Should clamp this so that `ia` and `ib` ++ // cannot select non-verts. ++ var ia = round((this._beginning) * last); ++ var ib = round((this._ending) * last); ++ ++ this._vertices.length = 0; ++ ++ for (var i = ia; i < ib + 1; i++) { ++ v = this.vertices[i]; ++ this._vertices.push(v); ++ } ++ ++ if (this._automatic) { ++ this.plot(); ++ } ++ ++ } ++ ++ Two.Shape.prototype._update.apply(this, arguments); ++ ++ return this; ++ ++ }, ++ ++ flagReset: function() { ++ ++ this._flagVertices = this._flagFill = this._flagStroke = ++ this._flagLinewidth = this._flagOpacity = this._flagVisible = ++ this._flagCap = this._flagJoin = this._flagMiter = ++ this._flagClip = false; ++ ++ Two.Shape.prototype.flagReset.call(this); ++ ++ return this; ++ ++ } ++ ++ }); ++ ++ Path.MakeObservable(Path.prototype); ++ ++ /** ++ * Utility functions ++ */ ++ ++ function getCurveLength(a, b, limit) { ++ // TODO: DRYness ++ var x1, x2, x3, x4, y1, y2, y3, y4; ++ ++ var right = b.controls && b.controls.right; ++ var left = a.controls && a.controls.left; ++ ++ x1 = b.x; ++ y1 = b.y; ++ x2 = (right || b).x; ++ y2 = (right || b).y; ++ x3 = (left || a).x; ++ y3 = (left || a).y; ++ x4 = a.x; ++ y4 = a.y; ++ ++ if (right && b._relative) { ++ x2 += b.x; ++ y2 += b.y; ++ } ++ ++ if (left && a._relative) { ++ x3 += a.x; ++ y3 += a.y; ++ } ++ ++ return Two.Utils.getCurveLength(x1, y1, x2, y2, x3, y3, x4, y4, limit); ++ ++ } ++ ++ function getSubdivisions(a, b, limit) { ++ // TODO: DRYness ++ var x1, x2, x3, x4, y1, y2, y3, y4; ++ ++ var right = b.controls && b.controls.right; ++ var left = a.controls && a.controls.left; ++ ++ x1 = b.x; ++ y1 = b.y; ++ x2 = (right || b).x; ++ y2 = (right || b).y; ++ x3 = (left || a).x; ++ y3 = (left || a).y; ++ x4 = a.x; ++ y4 = a.y; ++ ++ if (right && b._relative) { ++ x2 += b.x; ++ y2 += b.y; ++ } ++ ++ if (left && a._relative) { ++ x3 += a.x; ++ y3 += a.y; ++ } ++ ++ return Two.Utils.subdivide(x1, y1, x2, y2, x3, y3, x4, y4, limit); ++ ++ } ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ var Path = Two.Path; ++ var _ = Two.Utils; ++ ++ var Line = Two.Line = function(x1, y1, x2, y2) { ++ ++ var width = x2 - x1; ++ var height = y2 - y1; ++ ++ var w2 = width / 2; ++ var h2 = height / 2; ++ ++ Path.call(this, [ ++ new Two.Anchor(- w2, - h2), ++ new Two.Anchor(w2, h2) ++ ]); ++ ++ this.translation.set(x1 + w2, y1 + h2); ++ ++ }; ++ ++ _.extend(Line.prototype, Path.prototype); ++ ++ Path.MakeObservable(Line.prototype); ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ var Path = Two.Path; ++ var _ = Two.Utils; ++ ++ var Rectangle = Two.Rectangle = function(x, y, width, height) { ++ ++ Path.call(this, [ ++ new Two.Anchor(), ++ new Two.Anchor(), ++ new Two.Anchor(), ++ new Two.Anchor() ++ ], true); ++ ++ this.width = width; ++ this.height = height; ++ this._update(); ++ ++ this.translation.set(x, y); ++ ++ }; ++ ++ _.extend(Rectangle, { ++ ++ Properties: ['width', 'height'], ++ ++ MakeObservable: function(obj) { ++ Path.MakeObservable(obj); ++ _.each(Rectangle.Properties, Two.Utils.defineProperty, obj); ++ } ++ ++ }); ++ ++ _.extend(Rectangle.prototype, Path.prototype, { ++ ++ _width: 0, ++ _height: 0, ++ ++ _flagWidth: 0, ++ _flagHeight: 0, ++ ++ _update: function() { ++ ++ if (this._flagWidth || this._flagHeight) { ++ ++ var xr = this._width / 2; ++ var yr = this._height / 2; ++ ++ this.vertices[0].set(-xr, -yr); ++ this.vertices[1].set(xr, -yr); ++ this.vertices[2].set(xr, yr); ++ this.vertices[3].set(-xr, yr); ++ ++ } ++ ++ Path.prototype._update.call(this); ++ ++ return this; ++ ++ }, ++ ++ flagReset: function() { ++ ++ this._flagWidth = this._flagHeight = false; ++ Path.prototype.flagReset.call(this); ++ ++ return this; ++ ++ } ++ ++ }); ++ ++ Rectangle.MakeObservable(Rectangle.prototype); ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ var Path = Two.Path, TWO_PI = Math.PI * 2, cos = Math.cos, sin = Math.sin; ++ var _ = Two.Utils; ++ ++ var Ellipse = Two.Ellipse = function(ox, oy, rx, ry) { ++ ++ if (!_.isNumber(ry)) { ++ ry = rx; ++ } ++ ++ var amount = Two.Resolution; ++ ++ var points = _.map(_.range(amount), function(i) { ++ return new Two.Anchor(); ++ }, this); ++ ++ Path.call(this, points, true, true); ++ ++ this.width = rx * 2; ++ this.height = ry * 2; ++ ++ this._update(); ++ this.translation.set(ox, oy); ++ ++ }; ++ ++ _.extend(Ellipse, { ++ ++ Properties: ['width', 'height'], ++ ++ MakeObservable: function(obj) { ++ ++ Path.MakeObservable(obj); ++ _.each(Ellipse.Properties, Two.Utils.defineProperty, obj); ++ ++ } ++ ++ }); ++ ++ _.extend(Ellipse.prototype, Path.prototype, { ++ ++ _width: 0, ++ _height: 0, ++ ++ _flagWidth: false, ++ _flagHeight: false, ++ ++ _update: function() { ++ ++ if (this._flagWidth || this._flagHeight) { ++ for (var i = 0, l = this.vertices.length; i < l; i++) { ++ var pct = i / l; ++ var theta = pct * TWO_PI; ++ var x = this._width * cos(theta) / 2; ++ var y = this._height * sin(theta) / 2; ++ this.vertices[i].set(x, y); ++ } ++ } ++ ++ Path.prototype._update.call(this); ++ return this; ++ ++ }, ++ ++ flagReset: function() { ++ ++ this._flagWidth = this._flagHeight = false; ++ ++ Path.prototype.flagReset.call(this); ++ return this; ++ ++ } ++ ++ }); ++ ++ Ellipse.MakeObservable(Ellipse.prototype); ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ var Path = Two.Path, TWO_PI = Math.PI * 2, cos = Math.cos, sin = Math.sin; ++ var _ = Two.Utils; ++ ++ var Circle = Two.Circle = function(ox, oy, r) { ++ ++ var amount = Two.Resolution; ++ ++ var points = _.map(_.range(amount), function(i) { ++ return new Two.Anchor(); ++ }, this); ++ ++ Path.call(this, points, true, true); ++ ++ this.radius = r; ++ ++ this._update(); ++ this.translation.set(ox, oy); ++ ++ }; ++ ++ _.extend(Circle, { ++ ++ Properties: ['radius'], ++ ++ MakeObservable: function(obj) { ++ ++ Path.MakeObservable(obj); ++ _.each(Circle.Properties, Two.Utils.defineProperty, obj); ++ ++ } ++ ++ }); ++ ++ _.extend(Circle.prototype, Path.prototype, { ++ ++ _radius: 0, ++ _flagRadius: false, ++ ++ _update: function() { ++ ++ if (this._flagRadius) { ++ for (var i = 0, l = this.vertices.length; i < l; i++) { ++ var pct = i / l; ++ var theta = pct * TWO_PI; ++ var x = this._radius * cos(theta); ++ var y = this._radius * sin(theta); ++ this.vertices[i].set(x, y); ++ } ++ } ++ ++ Path.prototype._update.call(this); ++ return this; ++ ++ }, ++ ++ flagReset: function() { ++ ++ this._flagRadius = false; ++ ++ Path.prototype.flagReset.call(this); ++ return this; ++ ++ } ++ ++ }); ++ ++ Circle.MakeObservable(Circle.prototype); ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ var Path = Two.Path, TWO_PI = Math.PI * 2, cos = Math.cos, sin = Math.sin; ++ var _ = Two.Utils; ++ ++ var Polygon = Two.Polygon = function(ox, oy, r, sides) { ++ ++ sides = Math.max(sides || 0, 3); ++ ++ var points = _.map(_.range(sides), function(i) { ++ return new Two.Anchor(); ++ }); ++ ++ Path.call(this, points, true); ++ ++ this.width = r * 2; ++ this.height = r * 2; ++ this.sides = sides; ++ ++ this._update(); ++ this.translation.set(ox, oy); ++ ++ }; ++ ++ _.extend(Polygon, { ++ ++ Properties: ['width', 'height', 'sides'], ++ ++ MakeObservable: function(obj) { ++ ++ Path.MakeObservable(obj); ++ _.each(Polygon.Properties, Two.Utils.defineProperty, obj); ++ ++ } ++ ++ }); ++ ++ _.extend(Polygon.prototype, Path.prototype, { ++ ++ _width: 0, ++ _height: 0, ++ _sides: 0, ++ ++ _flagWidth: false, ++ _flagHeight: false, ++ _flagSides: false, ++ ++ _update: function() { ++ ++ if (this._flagWidth || this._flagHeight || this._flagSides) { ++ ++ var sides = this._sides; ++ var amount = this.vertices.length; ++ ++ if (amount > sides) { ++ this.vertices.splice(sides - 1, amount - sides); ++ } ++ ++ for (var i = 0; i < sides; i++) { ++ ++ var pct = (i + 0.5) / sides; ++ var theta = TWO_PI * pct + Math.PI / 2; ++ var x = this._width * cos(theta); ++ var y = this._height * sin(theta); ++ ++ if (i >= amount) { ++ this.vertices.push(new Two.Anchor(x, y)); ++ } else { ++ this.vertices[i].set(x, y); ++ } ++ ++ } ++ ++ } ++ ++ Path.prototype._update.call(this); ++ return this; ++ ++ }, ++ ++ flagReset: function() { ++ ++ this._flagWidth = this._flagHeight = this._flagSides = false; ++ Path.prototype.flagReset.call(this); ++ ++ return this; ++ ++ } ++ ++ }); ++ ++ Polygon.MakeObservable(Polygon.prototype); ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ var Path = Two.Path, PI = Math.PI, TWO_PI = Math.PI * 2, HALF_PI = Math.PI / 2, ++ cos = Math.cos, sin = Math.sin, abs = Math.abs, _ = Two.Utils; ++ ++ var ArcSegment = Two.ArcSegment = function(ox, oy, ir, or, sa, ea, res) { ++ ++ var points = _.map(_.range(res || (Two.Resolution * 3)), function() { ++ return new Two.Anchor(); ++ }); ++ ++ Path.call(this, points, false, false, true); ++ ++ this.innerRadius = ir; ++ this.outerRadius = or; ++ ++ this.startAngle = sa; ++ this.endAngle = ea; ++ ++ this._update(); ++ this.translation.set(ox, oy); ++ ++ } ++ ++ _.extend(ArcSegment, { ++ ++ Properties: ['startAngle', 'endAngle', 'innerRadius', 'outerRadius'], ++ ++ MakeObservable: function(obj) { ++ ++ Path.MakeObservable(obj); ++ _.each(ArcSegment.Properties, Two.Utils.defineProperty, obj); ++ ++ } ++ ++ }); ++ ++ _.extend(ArcSegment.prototype, Path.prototype, { ++ ++ _flagStartAngle: false, ++ _flagEndAngle: false, ++ _flagInnerRadius: false, ++ _flagOuterRadius: false, ++ ++ _startAngle: 0, ++ _endAngle: TWO_PI, ++ _innerRadius: 0, ++ _outerRadius: 0, ++ ++ _update: function() { ++ ++ if (this._flagStartAngle || this._flagEndAngle || this._flagInnerRadius ++ || this._flagOuterRadius) { ++ ++ var sa = this._startAngle; ++ var ea = this._endAngle; ++ ++ var ir = this._innerRadius; ++ var or = this._outerRadius; ++ ++ var connected = mod(sa, TWO_PI) === mod(ea, TWO_PI); ++ var punctured = ir > 0; ++ ++ var vertices = this.vertices; ++ var length = (punctured ? vertices.length / 2 : vertices.length); ++ var command, id = 0; ++ ++ if (connected) { ++ length--; ++ } else if (!punctured) { ++ length -= 2; ++ } ++ ++ /** ++ * Outer Circle ++ */ ++ for (var i = 0, last = length - 1; i < length; i++) { ++ ++ var pct = i / last; ++ var v = vertices[id]; ++ var theta = pct * (ea - sa) + sa; ++ var step = (ea - sa) / length; ++ ++ var x = or * Math.cos(theta); ++ var y = or * Math.sin(theta); ++ ++ switch (i) { ++ case 0: ++ command = Two.Commands.move; ++ break; ++ default: ++ command = Two.Commands.curve; ++ } ++ ++ v.command = command; ++ v.x = x; ++ v.y = y; ++ v.controls.left.clear(); ++ v.controls.right.clear(); ++ ++ if (v.command === Two.Commands.curve) { ++ var amp = or * step / Math.PI; ++ v.controls.left.x = amp * Math.cos(theta - HALF_PI); ++ v.controls.left.y = amp * Math.sin(theta - HALF_PI); ++ v.controls.right.x = amp * Math.cos(theta + HALF_PI); ++ v.controls.right.y = amp * Math.sin(theta + HALF_PI); ++ if (i === 1) { ++ v.controls.left.multiplyScalar(2); ++ } ++ if (i === last) { ++ v.controls.right.multiplyScalar(2); ++ } ++ } ++ ++ id++; ++ ++ } ++ ++ if (punctured) { ++ ++ if (connected) { ++ vertices[id].command = Two.Commands.close; ++ id++; ++ } else { ++ length--; ++ last = length - 1; ++ } ++ ++ /** ++ * Inner Circle ++ */ ++ for (i = 0; i < length; i++) { ++ ++ pct = i / last; ++ v = vertices[id]; ++ theta = (1 - pct) * (ea - sa) + sa; ++ step = (ea - sa) / length; ++ ++ x = ir * Math.cos(theta); ++ y = ir * Math.sin(theta); ++ command = Two.Commands.curve; ++ if (i <= 0) { ++ command = connected ? Two.Commands.move : Two.Commands.line; ++ } ++ ++ v.command = command; ++ v.x = x; ++ v.y = y; ++ v.controls.left.clear(); ++ v.controls.right.clear(); ++ ++ if (v.command === Two.Commands.curve) { ++ amp = ir * step / Math.PI; ++ v.controls.left.x = amp * Math.cos(theta + HALF_PI); ++ v.controls.left.y = amp * Math.sin(theta + HALF_PI); ++ v.controls.right.x = amp * Math.cos(theta - HALF_PI); ++ v.controls.right.y = amp * Math.sin(theta - HALF_PI); ++ if (i === 1) { ++ v.controls.left.multiplyScalar(2); ++ } ++ if (i === last) { ++ v.controls.right.multiplyScalar(2); ++ } ++ } ++ ++ id++; ++ ++ } ++ ++ } else if (!connected) { ++ ++ vertices[id].command = Two.Commands.line; ++ vertices[id].x = 0; ++ vertices[id].y = 0; ++ id++; ++ ++ } ++ ++ /** ++ * Final Point ++ */ ++ vertices[id].command = Two.Commands.close; ++ ++ } ++ ++ Path.prototype._update.call(this); ++ ++ return this; ++ ++ }, ++ ++ flagReset: function() { ++ ++ Path.prototype.flagReset.call(this); ++ ++ this._flagStartAngle = this._flagEndAngle ++ = this._flagInnerRadius = this._flagOuterRadius = false; ++ ++ return this; ++ ++ } ++ ++ }); ++ ++ ArcSegment.MakeObservable(ArcSegment.prototype); ++ ++ function mod(v, l) { ++ while (v < 0) { ++ v += l; ++ } ++ return v % l; ++ } ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ var Path = Two.Path, TWO_PI = Math.PI * 2, cos = Math.cos, sin = Math.sin; ++ var _ = Two.Utils; ++ ++ var Star = Two.Star = function(ox, oy, or, ir, sides) { ++ ++ if (!_.isNumber(ir)) { ++ ir = or / 2; ++ } ++ ++ if (!_.isNumber(sides) || sides <= 0) { ++ sides = 5; ++ } ++ ++ var length = sides * 2; ++ ++ var points = _.map(_.range(length), function(i) { ++ return new Two.Anchor(); ++ }); ++ ++ Path.call(this, points, true); ++ ++ this.innerRadius = ir; ++ this.outerRadius = or; ++ this.sides = sides; ++ ++ this._update(); ++ this.translation.set(ox, oy); ++ ++ }; ++ ++ _.extend(Star, { ++ ++ Properties: ['innerRadius', 'outerRadius', 'sides'], ++ ++ MakeObservable: function(obj) { ++ ++ Path.MakeObservable(obj); ++ _.each(Star.Properties, Two.Utils.defineProperty, obj); ++ ++ } ++ ++ }); ++ ++ _.extend(Star.prototype, Path.prototype, { ++ ++ _innerRadius: 0, ++ _outerRadius: 0, ++ _sides: 0, ++ ++ _flagInnerRadius: false, ++ _flagOuterRadius: false, ++ _flagSides: false, ++ ++ _update: function() { ++ ++ if (this._flagInnerRadius || this._flagOuterRadius || this._flagSides) { ++ ++ var sides = this._sides * 2; ++ var amount = this.vertices.length; ++ ++ if (amount > sides) { ++ this.vertices.splice(sides - 1, amount - sides); ++ } ++ ++ for (var i = 0; i < sides; i++) { ++ ++ var pct = (i + 0.5) / sides; ++ var theta = TWO_PI * pct; ++ var r = (i % 2 ? this._innerRadius : this._outerRadius); ++ var x = r * cos(theta); ++ var y = r * sin(theta); ++ ++ if (i >= amount) { ++ this.vertices.push(new Two.Anchor(x, y)); ++ } else { ++ this.vertices[i].set(x, y); ++ } ++ ++ } ++ ++ } ++ ++ Path.prototype._update.call(this); ++ ++ return this; ++ ++ }, ++ ++ flagReset: function() { ++ ++ this._flagInnerRadius = this._flagOuterRadius = this._flagSides = false; ++ Path.prototype.flagReset.call(this); ++ ++ return this; ++ ++ } ++ ++ }); ++ ++ Star.MakeObservable(Star.prototype); ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ var Path = Two.Path; ++ var _ = Two.Utils; ++ ++ var RoundedRectangle = Two.RoundedRectangle = function(ox, oy, width, height, radius) { ++ ++ if (!_.isNumber(radius)) { ++ radius = Math.floor(Math.min(width, height) / 12); ++ } ++ ++ var amount = 10; ++ ++ var points = _.map(_.range(amount), function(i) { ++ return new Two.Anchor(0, 0, 0, 0, 0, 0, ++ i === 0 ? Two.Commands.move : Two.Commands.curve); ++ }); ++ ++ points[points.length - 1].command = Two.Commands.close; ++ ++ Path.call(this, points, false, false, true); ++ ++ this.width = width; ++ this.height = height; ++ this.radius = radius; ++ ++ this._update(); ++ this.translation.set(ox, oy); ++ ++ }; ++ ++ _.extend(RoundedRectangle, { ++ ++ Properties: ['width', 'height', 'radius'], ++ ++ MakeObservable: function(obj) { ++ ++ Path.MakeObservable(obj); ++ _.each(RoundedRectangle.Properties, Two.Utils.defineProperty, obj); ++ ++ } ++ ++ }); ++ ++ _.extend(RoundedRectangle.prototype, Path.prototype, { ++ ++ _width: 0, ++ _height: 0, ++ _radius: 0, ++ ++ _flagWidth: false, ++ _flagHeight: false, ++ _flagRadius: false, ++ ++ _update: function() { ++ ++ if (this._flagWidth || this._flagHeight || this._flagRadius) { ++ ++ var width = this._width; ++ var height = this._height; ++ var radius = Math.min(Math.max(this._radius, 0), ++ Math.min(width, height)); ++ ++ var v; ++ var w = width / 2; ++ var h = height / 2; ++ ++ v = this.vertices[0]; ++ v.x = - (w - radius); ++ v.y = - h; ++ ++ // Upper Right Corner ++ ++ v = this.vertices[1]; ++ v.x = (w - radius); ++ v.y = - h; ++ v.controls.left.clear(); ++ v.controls.right.x = radius; ++ v.controls.right.y = 0; ++ ++ v = this.vertices[2]; ++ v.x = w; ++ v.y = - (h - radius); ++ v.controls.right.clear(); ++ v.controls.left.clear(); ++ ++ // Bottom Right Corner ++ ++ v = this.vertices[3]; ++ v.x = w; ++ v.y = (h - radius); ++ v.controls.left.clear(); ++ v.controls.right.x = 0; ++ v.controls.right.y = radius; ++ ++ v = this.vertices[4]; ++ v.x = (w - radius); ++ v.y = h; ++ v.controls.right.clear(); ++ v.controls.left.clear(); ++ ++ // Bottom Left Corner ++ ++ v = this.vertices[5]; ++ v.x = - (w - radius); ++ v.y = h; ++ v.controls.left.clear(); ++ v.controls.right.x = - radius; ++ v.controls.right.y = 0; ++ ++ v = this.vertices[6]; ++ v.x = - w; ++ v.y = (h - radius); ++ v.controls.left.clear(); ++ v.controls.right.clear(); ++ ++ // Upper Left Corner ++ ++ v = this.vertices[7]; ++ v.x = - w; ++ v.y = - (h - radius); ++ v.controls.left.clear(); ++ v.controls.right.x = 0; ++ v.controls.right.y = - radius; ++ ++ v = this.vertices[8]; ++ v.x = - (w - radius); ++ v.y = - h; ++ v.controls.left.clear(); ++ v.controls.right.clear(); ++ ++ v = this.vertices[9]; ++ v.copy(this.vertices[8]); ++ ++ } ++ ++ Path.prototype._update.call(this); ++ ++ return this; ++ ++ }, ++ ++ flagReset: function() { ++ ++ this._flagWidth = this._flagHeight = this._flagRadius = false; ++ Path.prototype.flagReset.call(this); ++ ++ return this; ++ ++ } ++ ++ }); ++ ++ RoundedRectangle.MakeObservable(RoundedRectangle.prototype); ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ var root = Two.root; ++ var getComputedMatrix = Two.Utils.getComputedMatrix; ++ var _ = Two.Utils; ++ ++ var canvas = (root.document ? root.document.createElement('canvas') : { getContext: _.identity }); ++ var ctx = canvas.getContext('2d'); ++ ++ var Text = Two.Text = function(message, x, y, styles) { ++ ++ Two.Shape.call(this); ++ ++ this._renderer.type = 'text'; ++ this._renderer.flagFill = _.bind(Text.FlagFill, this); ++ this._renderer.flagStroke = _.bind(Text.FlagStroke, this); ++ ++ this.value = message; ++ ++ if (_.isNumber(x)) { ++ this.translation.x = x; ++ } ++ if (_.isNumber(y)) { ++ this.translation.y = y; ++ } ++ ++ if (!_.isObject(styles)) { ++ return this; ++ } ++ ++ _.each(Two.Text.Properties, function(property) { ++ ++ if (property in styles) { ++ this[property] = styles[property]; ++ } ++ ++ }, this); ++ ++ }; ++ ++ _.extend(Two.Text, { ++ ++ Properties: [ ++ 'value', 'family', 'size', 'leading', 'alignment', 'linewidth', 'style', ++ 'weight', 'decoration', 'baseline', 'opacity', 'visible', 'fill', 'stroke' ++ ], ++ ++ FlagFill: function() { ++ this._flagFill = true; ++ }, ++ ++ FlagStroke: function() { ++ this._flagStroke = true; ++ }, ++ ++ MakeObservable: function(object) { ++ ++ Two.Shape.MakeObservable(object); ++ ++ _.each(Two.Text.Properties.slice(0, 12), Two.Utils.defineProperty, object); ++ ++ Object.defineProperty(object, 'fill', { ++ enumerable: true, ++ get: function() { ++ return this._fill; ++ }, ++ set: function(f) { ++ ++ if (this._fill instanceof Two.Gradient ++ || this._fill instanceof Two.LinearGradient ++ || this._fill instanceof Two.RadialGradient ++ || this._fill instanceof Two.Texture) { ++ this._fill.unbind(Two.Events.change, this._renderer.flagFill); ++ } ++ ++ this._fill = f; ++ this._flagFill = true; ++ ++ if (this._fill instanceof Two.Gradient ++ || this._fill instanceof Two.LinearGradient ++ || this._fill instanceof Two.RadialGradient ++ || this._fill instanceof Two.Texture) { ++ this._fill.bind(Two.Events.change, this._renderer.flagFill); ++ } ++ ++ } ++ }); ++ ++ Object.defineProperty(object, 'stroke', { ++ enumerable: true, ++ get: function() { ++ return this._stroke; ++ }, ++ set: function(f) { ++ ++ if (this._stroke instanceof Two.Gradient ++ || this._stroke instanceof Two.LinearGradient ++ || this._stroke instanceof Two.RadialGradient ++ || this._stroke instanceof Two.Texture) { ++ this._stroke.unbind(Two.Events.change, this._renderer.flagStroke); ++ } ++ ++ this._stroke = f; ++ this._flagStroke = true; ++ ++ if (this._stroke instanceof Two.Gradient ++ || this._stroke instanceof Two.LinearGradient ++ || this._stroke instanceof Two.RadialGradient ++ || this._stroke instanceof Two.Texture) { ++ this._stroke.bind(Two.Events.change, this._renderer.flagStroke); ++ } ++ ++ } ++ }); ++ ++ Object.defineProperty(object, 'clip', { ++ enumerable: true, ++ get: function() { ++ return this._clip; ++ }, ++ set: function(v) { ++ this._clip = v; ++ this._flagClip = true; ++ } ++ }); ++ ++ } ++ ++ }); ++ ++ _.extend(Two.Text.prototype, Two.Shape.prototype, { ++ ++ // Flags ++ // http://en.wikipedia.org/wiki/Flag ++ ++ _flagValue: true, ++ _flagFamily: true, ++ _flagSize: true, ++ _flagLeading: true, ++ _flagAlignment: true, ++ _flagBaseline: true, ++ _flagStyle: true, ++ _flagWeight: true, ++ _flagDecoration: true, ++ ++ _flagFill: true, ++ _flagStroke: true, ++ _flagLinewidth: true, ++ _flagOpacity: true, ++ _flagVisible: true, ++ ++ _flagClip: false, ++ ++ // Underlying Properties ++ ++ _value: '', ++ _family: 'sans-serif', ++ _size: 13, ++ _leading: 17, ++ _alignment: 'center', ++ _baseline: 'middle', ++ _style: 'normal', ++ _weight: 500, ++ _decoration: 'none', ++ ++ _fill: '#000', ++ _stroke: 'transparent', ++ _linewidth: 1, ++ _opacity: 1, ++ _visible: true, ++ ++ _clip: false, ++ ++ remove: function() { ++ ++ if (!this.parent) { ++ return this; ++ } ++ ++ this.parent.remove(this); ++ ++ return this; ++ ++ }, ++ ++ clone: function(parent) { ++ ++ var parent = parent || this.parent; ++ ++ var clone = new Two.Text(this.value); ++ clone.translation.copy(this.translation); ++ clone.rotation = this.rotation; ++ clone.scale = this.scale; ++ ++ _.each(Two.Text.Properties, function(property) { ++ clone[property] = this[property]; ++ }, this); ++ ++ if (parent) { ++ parent.add(clone); ++ } ++ ++ return clone; ++ ++ }, ++ ++ toObject: function() { ++ ++ var result = { ++ translation: this.translation.toObject(), ++ rotation: this.rotation, ++ scale: this.scale ++ }; ++ ++ _.each(Two.Text.Properties, function(property) { ++ result[property] = this[property]; ++ }, this); ++ ++ return result; ++ ++ }, ++ ++ noStroke: function() { ++ this.stroke = 'transparent'; ++ return this; ++ }, ++ ++ noFill: function() { ++ this.fill = 'transparent'; ++ return this; ++ }, ++ ++ /** ++ * A shim to not break `getBoundingClientRect` calls. TODO: Implement a ++ * way to calculate proper bounding boxes of `Two.Text`. ++ */ ++ getBoundingClientRect: function(shallow) { ++ ++ var matrix, border, l, x, y, i, v; ++ ++ var left = Infinity, right = -Infinity, ++ top = Infinity, bottom = -Infinity; ++ ++ // TODO: Update this to not __always__ update. Just when it needs to. ++ this._update(true); ++ ++ matrix = !!shallow ? this._matrix : getComputedMatrix(this); ++ ++ v = matrix.multiply(0, 0, 1); ++ ++ return { ++ top: v.x, ++ left: v.y, ++ right: v.x, ++ bottom: v.y, ++ width: 0, ++ height: 0 ++ }; ++ ++ }, ++ ++ flagReset: function() { ++ ++ this._flagValue = this._flagFamily = this._flagSize = ++ this._flagLeading = this._flagAlignment = this._flagFill = ++ this._flagStroke = this._flagLinewidth = this._flagOpaicty = ++ this._flagVisible = this._flagClip = this._flagDecoration = ++ this._flagBaseline = false; ++ ++ Two.Shape.prototype.flagReset.call(this); ++ ++ return this; ++ ++ } ++ ++ }); ++ ++ Two.Text.MakeObservable(Two.Text.prototype); ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ var _ = Two.Utils; ++ ++ var Stop = Two.Stop = function(offset, color, opacity) { ++ ++ this._renderer = {}; ++ this._renderer.type = 'stop'; ++ ++ this.offset = _.isNumber(offset) ? offset ++ : Stop.Index <= 0 ? 0 : 1; ++ ++ this.opacity = _.isNumber(opacity) ? opacity : 1; ++ ++ this.color = _.isString(color) ? color ++ : Stop.Index <= 0 ? '#fff' : '#000'; ++ ++ Stop.Index = (Stop.Index + 1) % 2; ++ ++ }; ++ ++ _.extend(Stop, { ++ ++ Index: 0, ++ ++ Properties: [ ++ 'offset', ++ 'opacity', ++ 'color' ++ ], ++ ++ MakeObservable: function(object) { ++ ++ _.each(Stop.Properties, function(property) { ++ ++ var object = this; ++ var secret = '_' + property; ++ var flag = '_flag' + property.charAt(0).toUpperCase() + property.slice(1); ++ ++ Object.defineProperty(object, property, { ++ enumerable: true, ++ get: function() { ++ return this[secret]; ++ }, ++ set: function(v) { ++ this[secret] = v; ++ this[flag] = true; ++ if (this.parent) { ++ this.parent._flagStops = true; ++ } ++ } ++ }); ++ ++ }, object); ++ ++ } ++ ++ }); ++ ++ _.extend(Stop.prototype, Two.Utils.Events, { ++ ++ clone: function() { ++ ++ var clone = new Stop(); ++ ++ _.each(Stop.Properties, function(property) { ++ clone[property] = this[property]; ++ }, this); ++ ++ return clone; ++ ++ }, ++ ++ toObject: function() { ++ ++ var result = {}; ++ ++ _.each(Stop.Properties, function(k) { ++ result[k] = this[k]; ++ }, this); ++ ++ return result; ++ ++ }, ++ ++ flagReset: function() { ++ ++ this._flagOffset = this._flagColor = this._flagOpacity = false; ++ ++ return this; ++ ++ } ++ ++ }); ++ ++ Stop.MakeObservable(Stop.prototype); ++ ++ var Gradient = Two.Gradient = function(stops) { ++ ++ this._renderer = {}; ++ this._renderer.type = 'gradient'; ++ ++ this.id = Two.Identifier + Two.uniqueId(); ++ this.classList = []; ++ ++ this._renderer.flagStops = _.bind(Gradient.FlagStops, this); ++ this._renderer.bindStops = _.bind(Gradient.BindStops, this); ++ this._renderer.unbindStops = _.bind(Gradient.UnbindStops, this); ++ ++ this.spread = 'pad'; ++ ++ this.stops = stops; ++ ++ }; ++ ++ _.extend(Gradient, { ++ ++ Stop: Stop, ++ ++ Properties: [ ++ 'spread' ++ ], ++ ++ MakeObservable: function(object) { ++ ++ _.each(Gradient.Properties, Two.Utils.defineProperty, object); ++ ++ Object.defineProperty(object, 'stops', { ++ ++ enumerable: true, ++ ++ get: function() { ++ return this._stops; ++ }, ++ ++ set: function(stops) { ++ ++ var updateStops = this._renderer.flagStops; ++ var bindStops = this._renderer.bindStops; ++ var unbindStops = this._renderer.unbindStops; ++ ++ // Remove previous listeners ++ if (this._stops) { ++ this._stops ++ .unbind(Two.Events.insert, bindStops) ++ .unbind(Two.Events.remove, unbindStops); ++ } ++ ++ // Create new Collection with copy of Stops ++ this._stops = new Two.Utils.Collection((stops || []).slice(0)); ++ ++ // Listen for Collection changes and bind / unbind ++ this._stops ++ .bind(Two.Events.insert, bindStops) ++ .bind(Two.Events.remove, unbindStops); ++ ++ // Bind Initial Stops ++ bindStops(this._stops); ++ ++ } ++ ++ }); ++ ++ }, ++ ++ FlagStops: function() { ++ this._flagStops = true; ++ }, ++ ++ BindStops: function(items) { ++ ++ // This function is called a lot ++ // when importing a large SVG ++ var i = items.length; ++ while(i--) { ++ items[i].bind(Two.Events.change, this._renderer.flagStops); ++ items[i].parent = this; ++ } ++ ++ this._renderer.flagStops(); ++ ++ }, ++ ++ UnbindStops: function(items) { ++ ++ var i = items.length; ++ while(i--) { ++ items[i].unbind(Two.Events.change, this._renderer.flagStops); ++ delete items[i].parent; ++ } ++ ++ this._renderer.flagStops(); ++ ++ } ++ ++ }); ++ ++ _.extend(Gradient.prototype, Two.Utils.Events, { ++ ++ _flagStops: false, ++ _flagSpread: false, ++ ++ clone: function(parent) { ++ ++ parent = parent || this.parent; ++ ++ var stops = _.map(this.stops, function(s) { ++ return s.clone(); ++ }); ++ ++ var clone = new Gradient(stops); ++ ++ _.each(Two.Gradient.Properties, function(k) { ++ clone[k] = this[k]; ++ }, this); ++ ++ if (parent) { ++ parent.add(clone); ++ } ++ ++ return clone; ++ ++ }, ++ ++ toObject: function() { ++ ++ var result = { ++ stops: _.map(this.stops, function(s) { ++ return s.toObject(); ++ }) ++ }; ++ ++ _.each(Gradient.Properties, function(k) { ++ result[k] = this[k]; ++ }, this); ++ ++ return result; ++ ++ }, ++ ++ _update: function() { ++ ++ if (this._flagSpread || this._flagStops) { ++ this.trigger(Two.Events.change); ++ } ++ ++ return this; ++ ++ }, ++ ++ flagReset: function() { ++ ++ this._flagSpread = this._flagStops = false; ++ ++ return this; ++ ++ } ++ ++ }); ++ ++ Gradient.MakeObservable(Gradient.prototype); ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ var _ = Two.Utils; ++ ++ var LinearGradient = Two.LinearGradient = function(x1, y1, x2, y2, stops) { ++ ++ Two.Gradient.call(this, stops); ++ ++ this._renderer.type = 'linear-gradient'; ++ ++ var flagEndPoints = _.bind(LinearGradient.FlagEndPoints, this); ++ this.left = new Two.Vector().bind(Two.Events.change, flagEndPoints); ++ this.right = new Two.Vector().bind(Two.Events.change, flagEndPoints); ++ ++ if (_.isNumber(x1)) { ++ this.left.x = x1; ++ } ++ if (_.isNumber(y1)) { ++ this.left.y = y1; ++ } ++ if (_.isNumber(x2)) { ++ this.right.x = x2; ++ } ++ if (_.isNumber(y2)) { ++ this.right.y = y2; ++ } ++ ++ }; ++ ++ _.extend(LinearGradient, { ++ ++ Stop: Two.Gradient.Stop, ++ ++ MakeObservable: function(object) { ++ Two.Gradient.MakeObservable(object); ++ }, ++ ++ FlagEndPoints: function() { ++ this._flagEndPoints = true; ++ } ++ ++ }); ++ ++ _.extend(LinearGradient.prototype, Two.Gradient.prototype, { ++ ++ _flagEndPoints: false, ++ ++ clone: function(parent) { ++ ++ parent = parent || this.parent; ++ ++ var stops = _.map(this.stops, function(stop) { ++ return stop.clone(); ++ }); ++ ++ var clone = new LinearGradient(this.left._x, this.left._y, ++ this.right._x, this.right._y, stops); ++ ++ _.each(Two.Gradient.Properties, function(k) { ++ clone[k] = this[k]; ++ }, this); ++ ++ if (parent) { ++ parent.add(clone); ++ } ++ ++ return clone; ++ ++ }, ++ ++ toObject: function() { ++ ++ var result = Two.Gradient.prototype.toObject.call(this); ++ ++ result.left = this.left.toObject(); ++ result.right = this.right.toObject(); ++ ++ return result; ++ ++ }, ++ ++ _update: function() { ++ ++ if (this._flagEndPoints || this._flagSpread || this._flagStops) { ++ this.trigger(Two.Events.change); ++ } ++ ++ return this; ++ ++ }, ++ ++ flagReset: function() { ++ ++ this._flagEndPoints = false; ++ ++ Two.Gradient.prototype.flagReset.call(this); ++ ++ return this; ++ ++ } ++ ++ }); ++ ++ LinearGradient.MakeObservable(LinearGradient.prototype); ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ var _ = Two.Utils; ++ ++ var RadialGradient = Two.RadialGradient = function(cx, cy, r, stops, fx, fy) { ++ ++ Two.Gradient.call(this, stops); ++ ++ this._renderer.type = 'radial-gradient'; ++ ++ this.center = new Two.Vector() ++ .bind(Two.Events.change, _.bind(function() { ++ this._flagCenter = true; ++ }, this)); ++ ++ this.radius = _.isNumber(r) ? r : 20; ++ ++ this.focal = new Two.Vector() ++ .bind(Two.Events.change, _.bind(function() { ++ this._flagFocal = true; ++ }, this)); ++ ++ if (_.isNumber(cx)) { ++ this.center.x = cx; ++ } ++ if (_.isNumber(cy)) { ++ this.center.y = cy; ++ } ++ ++ this.focal.copy(this.center); ++ ++ if (_.isNumber(fx)) { ++ this.focal.x = fx; ++ } ++ if (_.isNumber(fy)) { ++ this.focal.y = fy; ++ } ++ ++ }; ++ ++ _.extend(RadialGradient, { ++ ++ Stop: Two.Gradient.Stop, ++ ++ Properties: [ ++ 'radius' ++ ], ++ ++ MakeObservable: function(object) { ++ ++ Two.Gradient.MakeObservable(object); ++ ++ _.each(RadialGradient.Properties, Two.Utils.defineProperty, object); ++ ++ } ++ ++ }); ++ ++ _.extend(RadialGradient.prototype, Two.Gradient.prototype, { ++ ++ _flagRadius: false, ++ _flagCenter: false, ++ _flagFocal: false, ++ ++ clone: function(parent) { ++ ++ parent = parent || this.parent; ++ ++ var stops = _.map(this.stops, function(stop) { ++ return stop.clone(); ++ }); ++ ++ var clone = new RadialGradient(this.center._x, this.center._y, ++ this._radius, stops, this.focal._x, this.focal._y); ++ ++ _.each(Two.Gradient.Properties.concat(RadialGradient.Properties), function(k) { ++ clone[k] = this[k]; ++ }, this); ++ ++ if (parent) { ++ parent.add(clone); ++ } ++ ++ return clone; ++ ++ }, ++ ++ toObject: function() { ++ ++ var result = Two.Gradient.prototype.toObject.call(this); ++ ++ _.each(RadialGradient.Properties, function(k) { ++ result[k] = this[k]; ++ }, this); ++ ++ result.center = this.center.toObject(); ++ result.focal = this.focal.toObject(); ++ ++ return result; ++ ++ }, ++ ++ _update: function() { ++ ++ if (this._flagRadius || this._flatCenter || this._flagFocal ++ || this._flagSpread || this._flagStops) { ++ this.trigger(Two.Events.change); ++ } ++ ++ return this; ++ ++ }, ++ ++ flagReset: function() { ++ ++ this._flagRadius = this._flagCenter = this._flagFocal = false; ++ ++ Two.Gradient.prototype.flagReset.call(this); ++ ++ return this; ++ ++ } ++ ++ }); ++ ++ RadialGradient.MakeObservable(RadialGradient.prototype); ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ var _ = Two.Utils; ++ var anchor; ++ var regex = { ++ video: /\.(mp4|webm)$/i, ++ image: /\.(jpe?g|png|gif|tiff)$/i ++ }; ++ ++ if (this.document) { ++ anchor = document.createElement('a'); ++ } ++ ++ var Texture = Two.Texture = function(src, callback) { ++ ++ this._renderer = {}; ++ this._renderer.type = 'texture'; ++ this._renderer.flagOffset = _.bind(Texture.FlagOffset, this); ++ this._renderer.flagScale = _.bind(Texture.FlagScale, this); ++ ++ this.id = Two.Identifier + Two.uniqueId(); ++ this.classList = []; ++ ++ this.offset = new Two.Vector(); ++ ++ if (_.isFunction(callback)) { ++ var loaded = _.bind(function() { ++ this.unbind(Two.Events.load, loaded); ++ if (_.isFunction(callback)) { ++ callback(); ++ } ++ }, this); ++ this.bind(Two.Events.load, loaded); ++ } ++ ++ if (_.isString(src)) { ++ this.src = src; ++ } else if (_.isElement(src)) { ++ this.image = src; ++ } ++ ++ this._update(); ++ ++ }; ++ ++ _.extend(Texture, { ++ ++ Properties: [ ++ 'src', ++ 'loaded', ++ 'repeat' ++ ], ++ ++ ImageRegistry: new Two.Registry(), ++ ++ getAbsoluteURL: function(path) { ++ if (!anchor) { ++ // TODO: Fix for headless environment ++ return path; ++ } ++ anchor.href = path; ++ return anchor.href; ++ }, ++ ++ getImage: function(src) { ++ ++ var absoluteSrc = Texture.getAbsoluteURL(src); ++ ++ if (Texture.ImageRegistry.contains(absoluteSrc)) { ++ return Texture.ImageRegistry.get(absoluteSrc); ++ } ++ ++ var image; ++ ++ if (regex.video.test(absoluteSrc)) { ++ image = document.createElement('video'); ++ } else { ++ image = document.createElement('img'); ++ } ++ ++ image.crossOrigin = 'anonymous'; ++ ++ return image; ++ ++ }, ++ ++ Register: { ++ canvas: function(texture, callback) { ++ texture._src = '#' + texture.id; ++ Texture.ImageRegistry.add(texture.src, texture.image); ++ if (_.isFunction(callback)) { ++ callback(); ++ } ++ }, ++ img: function(texture, callback) { ++ ++ var loaded = function(e) { ++ texture.image.removeEventListener('load', loaded, false); ++ texture.image.removeEventListener('error', error, false); ++ if (_.isFunction(callback)) { ++ callback(); ++ } ++ }; ++ var error = function(e) { ++ texture.image.removeEventListener('load', loaded, false); ++ texture.image.removeEventListener('error', error, false); ++ throw new Two.Utils.Error('unable to load ' + texture.src); ++ }; ++ ++ if (_.isNumber(texture.image.width) && texture.image.width > 0 ++ && _.isNumber(texture.image.height) && texture.image.height > 0) { ++ loaded(); ++ } else { ++ texture.image.addEventListener('load', loaded, false); ++ texture.image.addEventListener('error', error, false); ++ } ++ ++ texture._src = Texture.getAbsoluteURL(texture._src); ++ ++ if (texture.image && texture.image.getAttribute('two-src')) { ++ return; ++ } ++ ++ texture.image.setAttribute('two-src', texture.src); ++ Texture.ImageRegistry.add(texture.src, texture.image); ++ texture.image.src = texture.src; ++ ++ }, ++ video: function(texture, callback) { ++ ++ var loaded = function(e) { ++ texture.image.removeEventListener('load', loaded, false); ++ texture.image.removeEventListener('error', error, false); ++ texture.image.width = texture.image.videoWidth; ++ texture.image.height = texture.image.videoHeight; ++ texture.image.play(); ++ if (_.isFunction(callback)) { ++ callback(); ++ } ++ }; ++ var error = function(e) { ++ texture.image.removeEventListener('load', loaded, false); ++ texture.image.removeEventListener('error', error, false); ++ throw new Two.Utils.Error('unable to load ' + texture.src); ++ }; ++ ++ texture._src = Texture.getAbsoluteURL(texture._src); ++ texture.image.addEventListener('canplaythrough', loaded, false); ++ texture.image.addEventListener('error', error, false); ++ ++ if (texture.image && texture.image.getAttribute('two-src')) { ++ return; ++ } ++ ++ texture.image.setAttribute('two-src', texture.src); ++ Texture.ImageRegistry.add(texture.src, texture.image); ++ texture.image.src = texture.src; ++ texture.image.loop = true; ++ texture.image.load(); ++ ++ } ++ }, ++ ++ load: function(texture, callback) { ++ ++ var src = texture.src; ++ var image = texture.image; ++ var tag = image && image.nodeName.toLowerCase(); ++ ++ if (texture._flagImage) { ++ if (/canvas/i.test(tag)) { ++ Texture.Register.canvas(texture, callback); ++ } else { ++ texture._src = image.getAttribute('two-src') || image.src; ++ Texture.Register[tag](texture, callback); ++ } ++ } ++ ++ if (texture._flagSrc) { ++ if (!image) { ++ texture.image = Texture.getImage(texture.src); ++ } ++ tag = texture.image.nodeName.toLowerCase(); ++ Texture.Register[tag](texture, callback); ++ } ++ ++ }, ++ ++ FlagOffset: function() { ++ this._flagOffset = true; ++ }, ++ ++ FlagScale: function() { ++ this._flagScale = true; ++ }, ++ ++ MakeObservable: function(object) { ++ ++ _.each(Texture.Properties, Two.Utils.defineProperty, object); ++ ++ Object.defineProperty(object, 'image', { ++ enumerable: true, ++ get: function() { ++ return this._image; ++ }, ++ set: function(image) { ++ ++ var tag = image && image.nodeName.toLowerCase(); ++ var index; ++ ++ switch (tag) { ++ case 'canvas': ++ index = '#' + image.id; ++ break; ++ default: ++ index = image.src; ++ } ++ ++ if (Texture.ImageRegistry.contains(index)) { ++ this._image = Texture.ImageRegistry.get(image.src); ++ } else { ++ this._image = image; ++ } ++ ++ this._flagImage = true; ++ ++ } ++ ++ }); ++ ++ Object.defineProperty(object, 'offset', { ++ enumerable: true, ++ get: function() { ++ return this._offset; ++ }, ++ set: function(v) { ++ if (this._offset) { ++ this._offset.unbind(Two.Events.change, this._renderer.flagOffset); ++ } ++ this._offset = v; ++ this._offset.bind(Two.Events.change, this._renderer.flagOffset); ++ this._flagOffset = true; ++ } ++ }); ++ ++ Object.defineProperty(object, 'scale', { ++ enumerable: true, ++ get: function() { ++ return this._scale; ++ }, ++ set: function(v) { ++ ++ if (this._scale instanceof Two.Vector) { ++ this._scale.unbind(Two.Events.change, this._renderer.flagScale); ++ } ++ ++ this._scale = v; ++ ++ if (this._scale instanceof Two.Vector) { ++ this._scale.bind(Two.Events.change, this._renderer.flagScale); ++ } ++ ++ this._flagScale = true; ++ ++ } ++ }); ++ ++ } ++ ++ }); ++ ++ _.extend(Texture.prototype, Two.Utils.Events, Two.Shape.prototype, { ++ ++ _flagSrc: false, ++ _flagImage: false, ++ _flagVideo: false, ++ _flagLoaded: false, ++ _flagRepeat: false, ++ ++ _flagOffset: false, ++ _flagScale: false, ++ ++ _src: '', ++ _image: null, ++ _loaded: false, ++ _repeat: 'no-repeat', ++ ++ _scale: 1, ++ _offset: null, ++ ++ clone: function() { ++ return new Texture(this.src); ++ }, ++ ++ toObject: function() { ++ return { ++ src: this.src, ++ image: this.image ++ } ++ }, ++ ++ _update: function() { ++ ++ if (this._flagSrc || this._flagImage || this._flagVideo) { ++ ++ this.trigger(Two.Events.change); ++ ++ if (this._flagSrc || this._flagImage) { ++ this.loaded = false; ++ Texture.load(this, _.bind(function() { ++ this.loaded = true; ++ this ++ .trigger(Two.Events.change) ++ .trigger(Two.Events.load); ++ }, this)); ++ } ++ ++ } ++ ++ if (this._image && this._image.readyState >= 4) { ++ this._flagVideo = true; ++ } ++ ++ return this; ++ ++ }, ++ ++ flagReset: function() { ++ ++ this._flagSrc = this._flagImage = this._flagLoaded ++ = this._flagVideo = this._flagScale = this._flagOffset = false; ++ ++ return this; ++ ++ } ++ ++ }); ++ ++ Texture.MakeObservable(Texture.prototype); ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ var _ = Two.Utils; ++ var Path = Two.Path; ++ var Rectangle = Two.Rectangle; ++ ++ var Sprite = Two.Sprite = function(path, ox, oy, cols, rows, frameRate) { ++ ++ Path.call(this, [ ++ new Two.Anchor(), ++ new Two.Anchor(), ++ new Two.Anchor(), ++ new Two.Anchor() ++ ], true); ++ ++ this.noStroke(); ++ this.noFill(); ++ ++ if (path instanceof Two.Texture) { ++ this.texture = path; ++ } else if (_.isString(path)) { ++ this.texture = new Two.Texture(path); ++ } ++ ++ this._update(); ++ this.translation.set(ox || 0, oy || 0); ++ ++ if (_.isNumber(cols)) { ++ this.columns = cols; ++ } ++ if (_.isNumber(rows)) { ++ this.rows = rows; ++ } ++ if (_.isNumber(frameRate)) { ++ this.frameRate = frameRate; ++ } ++ ++ }; ++ ++ _.extend(Sprite, { ++ ++ Properties: [ ++ 'texture', 'columns', 'rows', 'frameRate', 'index' ++ ], ++ ++ MakeObservable: function(obj) { ++ ++ Rectangle.MakeObservable(obj); ++ _.each(Sprite.Properties, Two.Utils.defineProperty, obj); ++ ++ } ++ ++ }) ++ ++ _.extend(Sprite.prototype, Rectangle.prototype, { ++ ++ _flagTexture: false, ++ _flagColumns: false, ++ _flagRows: false, ++ _flagFrameRate: false, ++ flagIndex: false, ++ ++ // Private variables ++ _amount: 1, ++ _duration: 0, ++ _startTime: 0, ++ _playing: false, ++ _firstFrame: 0, ++ _lastFrame: 0, ++ _loop: true, ++ ++ // Exposed through getter-setter ++ _texture: null, ++ _columns: 1, ++ _rows: 1, ++ _frameRate: 0, ++ _index: 0, ++ ++ play: function(firstFrame, lastFrame, onLastFrame) { ++ ++ this._playing = true; ++ this._firstFrame = 0; ++ this._lastFrame = this.amount - 1; ++ this._startTime = _.performance.now(); ++ ++ if (_.isNumber(firstFrame)) { ++ this._firstFrame = firstFrame; ++ } ++ if (_.isNumber(lastFrame)) { ++ this._lastFrame = lastFrame; ++ } ++ if (_.isFunction(onLastFrame)) { ++ this._onLastFrame = onLastFrame; ++ } else { ++ delete this._onLastFrame; ++ } ++ ++ if (this._index !== this._firstFrame) { ++ this._startTime -= 1000 * Math.abs(this._index - this._firstFrame) ++ / this._frameRate; ++ } ++ ++ return this; ++ ++ }, ++ ++ pause: function() { ++ ++ this._playing = false; ++ return this; ++ ++ }, ++ ++ stop: function() { ++ ++ this._playing = false; ++ this._index = 0; ++ ++ return this; ++ ++ }, ++ ++ clone: function(parent) { ++ ++ parent = parent || this.parent; ++ ++ var clone = new Sprite( ++ this.texture, this.translation.x, this.translation.y, ++ this.columns, this.rows, this.frameRate ++ ); ++ ++ if (this.playing) { ++ clone.play(this._firstFrame, this._lastFrame); ++ clone._loop = this._loop; ++ } ++ ++ if (parent) { ++ parent.add(clone); ++ } ++ ++ return clone; ++ ++ }, ++ ++ _update: function() { ++ ++ var effect = this._texture; ++ var cols = this._columns; ++ var rows = this._rows; ++ ++ var width, height, elapsed, amount, duration; ++ var index, iw, ih, isRange, frames; ++ ++ if (this._flagColumns || this._flagRows) { ++ this._amount = this._columns * this._rows; ++ } ++ ++ if (this._flagFrameRate) { ++ this._duration = 1000 * this._amount / this._frameRate; ++ } ++ ++ if (this._flagTexture) { ++ this.fill = this._texture; ++ } ++ ++ if (this._texture.loaded) { ++ ++ iw = effect.image.width; ++ ih = effect.image.height; ++ ++ width = iw / cols; ++ height = ih / rows; ++ amount = this._amount; ++ ++ if (this.width !== width) { ++ this.width = width; ++ } ++ if (this.height !== height) { ++ this.height = height; ++ } ++ ++ if (this._playing && this._frameRate > 0) { ++ ++ if (_.isNaN(this._lastFrame)) { ++ this._lastFrame = amount - 1; ++ } ++ ++ // TODO: Offload perf logic to instance of `Two`. ++ elapsed = _.performance.now() - this._startTime; ++ frames = this._lastFrame + 1; ++ duration = 1000 * (frames - this._firstFrame) / this._frameRate; ++ ++ if (this._loop) { ++ elapsed = elapsed % duration; ++ } else { ++ elapsed = Math.min(elapsed, duration); ++ } ++ ++ index = _.lerp(this._firstFrame, frames, elapsed / duration); ++ index = Math.floor(index); ++ ++ if (index !== this._index) { ++ this._index = index; ++ if (index >= this._lastFrame - 1 && this._onLastFrame) { ++ this._onLastFrame(); // Shortcut for chainable sprite animations ++ } ++ } ++ ++ } ++ ++ var col = this._index % cols; ++ var row = Math.floor(this._index / cols); ++ ++ var ox = - width * col + (iw - width) / 2; ++ var oy = - height * row + (ih - height) / 2; ++ ++ // TODO: Improve performance ++ if (ox !== effect.offset.x) { ++ effect.offset.x = ox; ++ } ++ if (oy !== effect.offset.y) { ++ effect.offset.y = oy; ++ } ++ ++ } ++ ++ Rectangle.prototype._update.call(this); ++ ++ return this; ++ ++ }, ++ ++ flagReset: function() { ++ ++ this._flagTexture = this._flagColumns = this._flagRows ++ = this._flagFrameRate = false; ++ ++ Rectangle.prototype.flagReset.call(this); ++ ++ return this; ++ } ++ ++ ++ }); ++ ++ Sprite.MakeObservable(Sprite.prototype); ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ var _ = Two.Utils; ++ var Path = Two.Path; ++ var Rectangle = Two.Rectangle; ++ ++ var ImageSequence = Two.ImageSequence = function(paths, ox, oy, frameRate) { ++ ++ Path.call(this, [ ++ new Two.Anchor(), ++ new Two.Anchor(), ++ new Two.Anchor(), ++ new Two.Anchor() ++ ], true); ++ ++ this._renderer.flagTextures = _.bind(ImageSequence.FlagTextures, this); ++ this._renderer.bindTextures = _.bind(ImageSequence.BindTextures, this); ++ this._renderer.unbindTextures = _.bind(ImageSequence.UnbindTextures, this); ++ ++ this.noStroke(); ++ this.noFill(); ++ ++ this.textures = _.map(paths, ImageSequence.GenerateTexture, this); ++ ++ this._update(); ++ this.translation.set(ox || 0, oy || 0); ++ ++ if (_.isNumber(frameRate)) { ++ this.frameRate = frameRate; ++ } else { ++ this.frameRate = ImageSequence.DefaultFrameRate; ++ } ++ ++ }; ++ ++ _.extend(ImageSequence, { ++ ++ Properties: [ ++ 'frameRate', ++ 'index' ++ ], ++ ++ DefaultFrameRate: 30, ++ ++ FlagTextures: function() { ++ this._flagTextures = true; ++ }, ++ ++ BindTextures: function(items) { ++ ++ var i = items.length; ++ while (i--) { ++ items[i].bind(Two.Events.change, this._renderer.flagTextures); ++ } ++ ++ this._renderer.flagTextures(); ++ ++ }, ++ ++ UnbindTextures: function(items) { ++ ++ var i = items.length; ++ while (i--) { ++ items[i].unbind(Two.Events.change, this._renderer.flagTextures); ++ } ++ ++ this._renderer.flagTextures(); ++ ++ }, ++ ++ MakeObservable: function(obj) { ++ ++ Rectangle.MakeObservable(obj); ++ _.each(ImageSequence.Properties, Two.Utils.defineProperty, obj); ++ ++ Object.defineProperty(obj, 'textures', { ++ ++ enumerable: true, ++ ++ get: function() { ++ return this._textures; ++ }, ++ ++ set: function(textures) { ++ ++ var updateTextures = this._renderer.flagTextures; ++ var bindTextures = this._renderer.bindTextures; ++ var unbindTextures = this._renderer.unbindTextures; ++ ++ // Remove previous listeners ++ if (this._textures) { ++ this._textures ++ .unbind(Two.Events.insert, bindTextures) ++ .unbind(Two.Events.remove, unbindTextures); ++ } ++ ++ // Create new Collection with copy of vertices ++ this._textures = new Two.Utils.Collection((textures || []).slice(0)); ++ ++ // Listen for Collection changes and bind / unbind ++ this._textures ++ .bind(Two.Events.insert, bindTextures) ++ .bind(Two.Events.remove, unbindTextures); ++ ++ // Bind Initial Textures ++ bindTextures(this._textures); ++ ++ } ++ ++ }); ++ ++ }, ++ ++ GenerateTexture: function(obj) { ++ if (obj instanceof Two.Texture) { ++ return obj; ++ } else if (_.isString(obj)) { ++ return new Two.Texture(obj); ++ } ++ } ++ ++ }); ++ ++ _.extend(ImageSequence.prototype, Rectangle.prototype, { ++ ++ _flagTextures: false, ++ _flagFrameRate: false, ++ _flagIndex: false, ++ ++ // Private variables ++ _amount: 1, ++ _duration: 0, ++ _index: 0, ++ _startTime: 0, ++ _playing: false, ++ _firstFrame: 0, ++ _lastFrame: 0, ++ _loop: true, ++ ++ // Exposed through getter-setter ++ _textures: null, ++ _frameRate: 0, ++ ++ play: function(firstFrame, lastFrame, onLastFrame) { ++ ++ this._playing = true; ++ this._firstFrame = 0; ++ this._lastFrame = this.amount - 1; ++ this._startTime = _.performance.now(); ++ ++ if (_.isNumber(firstFrame)) { ++ this._firstFrame = firstFrame; ++ } ++ if (_.isNumber(lastFrame)) { ++ this._lastFrame = lastFrame; ++ } ++ if (_.isFunction(onLastFrame)) { ++ this._onLastFrame = onLastFrame; ++ } else { ++ delete this._onLastFrame; ++ } ++ ++ if (this._index !== this._firstFrame) { ++ this._startTime -= 1000 * Math.abs(this._index - this._firstFrame) ++ / this._frameRate; ++ } ++ ++ return this; ++ ++ }, ++ ++ pause: function() { ++ ++ this._playing = false; ++ return this; ++ ++ }, ++ ++ stop: function() { ++ ++ this._playing = false; ++ this._index = 0; ++ ++ return this; ++ ++ }, ++ ++ clone: function(parent) { ++ ++ parent = parent || this.parent; ++ ++ var clone = new ImageSequence(this.textures, this.translation.x, ++ this.translation.y, this.frameRate) ++ ++ clone._loop = this._loop; ++ ++ if (this._playing) { ++ clone.play(); ++ } ++ ++ if (parent) { ++ parent.add(clone); ++ } ++ ++ return clone; ++ ++ }, ++ ++ _update: function() { ++ ++ var effects = this._textures; ++ var width, height, elapsed, amount, duration, texture; ++ var index, frames; ++ ++ if (this._flagTextures) { ++ this._amount = effects.length; ++ } ++ ++ if (this._flagFrameRate) { ++ this._duration = 1000 * this._amount / this._frameRate; ++ } ++ ++ if (this._playing && this._frameRate > 0) { ++ ++ amount = this._amount; ++ ++ if (_.isNaN(this._lastFrame)) { ++ this._lastFrame = amount - 1; ++ } ++ ++ // TODO: Offload perf logic to instance of `Two`. ++ elapsed = _.performance.now() - this._startTime; ++ frames = this._lastFrame + 1; ++ duration = 1000 * (frames - this._firstFrame) / this._frameRate; ++ ++ if (this._loop) { ++ elapsed = elapsed % duration; ++ } else { ++ elapsed = Math.min(elapsed, duration); ++ } ++ ++ index = _.lerp(this._firstFrame, frames, elapsed / duration); ++ index = Math.floor(index); ++ ++ if (index !== this._index) { ++ ++ this._index = index; ++ texture = effects[this._index]; ++ ++ if (texture.loaded) { ++ ++ width = texture.image.width; ++ height = texture.image.height; ++ ++ if (this.width !== width) { ++ this.width = width; ++ } ++ if (this.height !== height) { ++ this.height = height; ++ } ++ ++ this.fill = texture; ++ ++ if (index >= this._lastFrame - 1 && this._onLastFrame) { ++ this._onLastFrame(); // Shortcut for chainable sprite animations ++ } ++ ++ } ++ ++ } ++ ++ } else if (this._flagIndex || !(this.fill instanceof Two.Texture)) { ++ ++ texture = effects[this._index]; ++ ++ if (texture.loaded) { ++ ++ width = texture.image.width; ++ height = texture.image.height; ++ ++ if (this.width !== width) { ++ this.width = width; ++ } ++ if (this.height !== height) { ++ this.height = height; ++ } ++ ++ } ++ ++ this.fill = texture; ++ ++ } ++ ++ Rectangle.prototype._update.call(this); ++ ++ return this; ++ ++ }, ++ ++ flagReset: function() { ++ ++ this._flagTextures = this._flagFrameRate = false; ++ Rectangle.prototype.flagReset.call(this); ++ ++ return this; ++ ++ } ++ ++ }); ++ ++ ImageSequence.MakeObservable(ImageSequence.prototype); ++ ++})((typeof global !== 'undefined' ? global : this).Two); ++ ++(function(Two) { ++ ++ /** ++ * Constants ++ */ ++ var min = Math.min, max = Math.max; ++ var _ = Two.Utils; ++ ++ /** ++ * A children collection which is accesible both by index and by object id ++ * @constructor ++ */ ++ var Children = function() { ++ ++ Two.Utils.Collection.apply(this, arguments); ++ ++ Object.defineProperty(this, '_events', { ++ value : {}, ++ enumerable: false ++ }); ++ ++ this.ids = {}; ++ ++ this.on(Two.Events.insert, this.attach); ++ this.on(Two.Events.remove, this.detach); ++ Children.prototype.attach.apply(this, arguments); ++ ++ }; ++ ++ Children.prototype = new Two.Utils.Collection(); ++ Children.prototype.constructor = Children; ++ ++ _.extend(Children.prototype, { ++ ++ attach: function(children) { ++ for (var i = 0; i < children.length; i++) { ++ this.ids[children[i].id] = children[i]; ++ } ++ return this; ++ }, ++ ++ detach: function(children) { ++ for (var i = 0; i < children.length; i++) { ++ delete this.ids[children[i].id]; ++ } ++ return this; ++ } ++ ++ }); ++ ++ var Group = Two.Group = function() { ++ ++ Two.Shape.call(this, true); ++ ++ this._renderer.type = 'group'; ++ ++ this.additions = []; ++ this.subtractions = []; ++ ++ this.children = arguments; ++ ++ }; ++ ++ _.extend(Group, { ++ ++ Children: Children, ++ ++ InsertChildren: function(children) { ++ for (var i = 0; i < children.length; i++) { ++ replaceParent.call(this, children[i], this); ++ } ++ }, ++ ++ RemoveChildren: function(children) { ++ for (var i = 0; i < children.length; i++) { ++ replaceParent.call(this, children[i]); ++ } ++ }, ++ ++ OrderChildren: function(children) { ++ this._flagOrder = true; ++ }, ++ ++ MakeObservable: function(object) { ++ ++ var properties = Two.Path.Properties.slice(0); ++ var oi = _.indexOf(properties, 'opacity'); ++ ++ if (oi >= 0) { ++ ++ properties.splice(oi, 1); ++ ++ Object.defineProperty(object, 'opacity', { ++ ++ enumerable: true, ++ ++ get: function() { ++ return this._opacity; ++ }, ++ ++ set: function(v) { ++ // Only set flag if there is an actual difference ++ this._flagOpacity = (this._opacity != v); ++ this._opacity = v; ++ } ++ ++ }); ++ ++ } ++ ++ Two.Shape.MakeObservable(object); ++ Group.MakeGetterSetters(object, properties); ++ ++ Object.defineProperty(object, 'children', { ++ ++ enumerable: true, ++ ++ get: function() { ++ return this._children; ++ }, ++ ++ set: function(children) { ++ ++ var insertChildren = _.bind(Group.InsertChildren, this); ++ var removeChildren = _.bind(Group.RemoveChildren, this); ++ var orderChildren = _.bind(Group.OrderChildren, this); ++ ++ if (this._children) { ++ this._children.unbind(); ++ } ++ ++ this._children = new Children(children); ++ this._children.bind(Two.Events.insert, insertChildren); ++ this._children.bind(Two.Events.remove, removeChildren); ++ this._children.bind(Two.Events.order, orderChildren); ++ ++ } ++ ++ }); ++ ++ Object.defineProperty(object, 'mask', { ++ ++ enumerable: true, ++ ++ get: function() { ++ return this._mask; ++ }, ++ ++ set: function(v) { ++ this._mask = v; ++ this._flagMask = true; ++ if (!v.clip) { ++ v.clip = true; ++ } ++ } ++ ++ }); ++ ++ }, ++ ++ MakeGetterSetters: function(group, properties) { ++ ++ if (!_.isArray(properties)) { ++ properties = [properties]; ++ } ++ ++ _.each(properties, function(k) { ++ Group.MakeGetterSetter(group, k); ++ }); ++ ++ }, ++ ++ MakeGetterSetter: function(group, k) { ++ ++ var secret = '_' + k; ++ ++ Object.defineProperty(group, k, { ++ ++ enumerable: true, ++ ++ get: function() { ++ return this[secret]; ++ }, ++ ++ set: function(v) { ++ this[secret] = v; ++ _.each(this.children, function(child) { // Trickle down styles ++ child[k] = v; ++ }); ++ } ++ ++ }); ++ ++ } ++ ++ }); ++ ++ _.extend(Group.prototype, Two.Shape.prototype, { ++ ++ // Flags ++ // http://en.wikipedia.org/wiki/Flag ++ ++ _flagAdditions: false, ++ _flagSubtractions: false, ++ _flagOrder: false, ++ _flagOpacity: true, ++ ++ _flagMask: false, ++ ++ // Underlying Properties ++ ++ _fill: '#fff', ++ _stroke: '#000', ++ _linewidth: 1.0, ++ _opacity: 1.0, ++ _visible: true, ++ ++ _cap: 'round', ++ _join: 'round', ++ _miter: 4, ++ ++ _closed: true, ++ _curved: false, ++ _automatic: true, ++ _beginning: 0, ++ _ending: 1.0, ++ ++ _mask: null, ++ ++ /** ++ * TODO: Group has a gotcha in that it's at the moment required to be bound to ++ * an instance of two in order to add elements correctly. This needs to ++ * be rethought and fixed. ++ */ ++ clone: function(parent) { ++ ++ parent = parent || this.parent; ++ ++ var group = new Group(); ++ var children = _.map(this.children, function(child) { ++ return child.clone(group); ++ }); ++ ++ group.add(children); ++ ++ group.opacity = this.opacity; ++ ++ if (this.mask) { ++ group.mask = this.mask; ++ } ++ ++ group.translation.copy(this.translation); ++ group.rotation = this.rotation; ++ group.scale = this.scale; ++ ++ if (parent) { ++ parent.add(group); ++ } ++ ++ return group; ++ ++ }, ++ ++ /** ++ * Export the data from the instance of Two.Group into a plain JavaScript ++ * object. This also makes all children plain JavaScript objects. Great ++ * for turning into JSON and storing in a database. ++ */ ++ toObject: function() { ++ ++ var result = { ++ children: [], ++ translation: this.translation.toObject(), ++ rotation: this.rotation, ++ scale: this.scale, ++ opacity: this.opacity, ++ mask: (this.mask ? this.mask.toObject() : null) ++ }; ++ ++ _.each(this.children, function(child, i) { ++ result.children[i] = child.toObject(); ++ }, this); ++ ++ return result; ++ ++ }, ++ ++ /** ++ * Anchor all children to the upper left hand corner ++ * of the group. ++ */ ++ corner: function() { ++ ++ var rect = this.getBoundingClientRect(true), ++ corner = { x: rect.left, y: rect.top }; ++ ++ this.children.forEach(function(child) { ++ child.translation.subSelf(corner); ++ }); ++ ++ return this; ++ ++ }, ++ ++ /** ++ * Anchors all children around the center of the group, ++ * effectively placing the shape around the unit circle. ++ */ ++ center: function() { ++ ++ var rect = this.getBoundingClientRect(true); ++ ++ rect.centroid = { ++ x: rect.left + rect.width / 2, ++ y: rect.top + rect.height / 2 ++ }; ++ ++ this.children.forEach(function(child) { ++ if (child.isShape) { ++ child.translation.subSelf(rect.centroid); ++ } ++ }); ++ ++ // this.translation.copy(rect.centroid); ++ ++ return this; ++ ++ }, ++ ++ /** ++ * Recursively search for id. Returns the first element found. ++ * Returns null if none found. ++ */ ++ getById: function (id) { ++ var search = function (node, id) { ++ if (node.id === id) { ++ return node; ++ } else if (node.children) { ++ var i = node.children.length; ++ while (i--) { ++ var found = search(node.children[i], id); ++ if (found) return found; ++ } ++ } ++ ++ }; ++ return search(this, id) || null; ++ }, ++ ++ /** ++ * Recursively search for classes. Returns an array of matching elements. ++ * Empty array if none found. ++ */ ++ getByClassName: function (cl) { ++ var found = []; ++ var search = function (node, cl) { ++ if (node.classList.indexOf(cl) != -1) { ++ found.push(node); ++ } else if (node.children) { ++ node.children.forEach(function (child) { ++ search(child, cl); ++ }); ++ } ++ return found; ++ }; ++ return search(this, cl); ++ }, ++ ++ /** ++ * Recursively search for children of a specific type, ++ * e.g. Two.Polygon. Pass a reference to this type as the param. ++ * Returns an empty array if none found. ++ */ ++ getByType: function(type) { ++ var found = []; ++ var search = function (node, type) { ++ for (var id in node.children) { ++ if (node.children[id] instanceof type) { ++ found.push(node.children[id]); ++ } else if (node.children[id] instanceof Two.Group) { ++ search(node.children[id], type); ++ } ++ } ++ return found; ++ }; ++ return search(this, type); ++ }, ++ ++ /** ++ * Add objects to the group. ++ */ ++ add: function(objects) { ++ ++ // Allow to pass multiple objects either as array or as multiple arguments ++ // If it's an array also create copy of it in case we're getting passed ++ // a childrens array directly. ++ if (!(objects instanceof Array)) { ++ objects = _.toArray(arguments); ++ } else { ++ objects = objects.slice(); ++ } ++ ++ // Add the objects ++ for (var i = 0; i < objects.length; i++) { ++ if (!(objects[i] && objects[i].id)) continue; ++ this.children.push(objects[i]); ++ } ++ ++ return this; ++ ++ }, ++ ++ /** ++ * Remove objects from the group. ++ */ ++ remove: function(objects) { ++ ++ var l = arguments.length, ++ grandparent = this.parent; ++ ++ // Allow to call remove without arguments ++ // This will detach the object from the scene. ++ if (l <= 0 && grandparent) { ++ grandparent.remove(this); ++ return this; ++ } ++ ++ // Allow to pass multiple objects either as array or as multiple arguments ++ // If it's an array also create copy of it in case we're getting passed ++ // a childrens array directly. ++ if (!(objects instanceof Array)) { ++ objects = _.toArray(arguments); ++ } else { ++ objects = objects.slice(); ++ } ++ ++ // Remove the objects ++ for (var i = 0; i < objects.length; i++) { ++ if (!objects[i] || !(this.children.ids[objects[i].id])) continue; ++ this.children.splice(_.indexOf(this.children, objects[i]), 1); ++ } ++ ++ return this; ++ ++ }, ++ ++ /** ++ * Return an object with top, left, right, bottom, width, and height ++ * parameters of the group. ++ */ ++ getBoundingClientRect: function(shallow) { ++ var rect; ++ ++ // TODO: Update this to not __always__ update. Just when it needs to. ++ this._update(true); ++ ++ // Variables need to be defined here, because of nested nature of groups. ++ var left = Infinity, right = -Infinity, ++ top = Infinity, bottom = -Infinity; ++ ++ this.children.forEach(function(child) { ++ ++ if (/(linear-gradient|radial-gradient|gradient)/.test(child._renderer.type)) { ++ return; ++ } ++ ++ rect = child.getBoundingClientRect(shallow); ++ ++ if (!_.isNumber(rect.top) || !_.isNumber(rect.left) || ++ !_.isNumber(rect.right) || !_.isNumber(rect.bottom)) { ++ return; ++ } ++ ++ top = min(rect.top, top); ++ left = min(rect.left, left); ++ right = max(rect.right, right); ++ bottom = max(rect.bottom, bottom); ++ ++ }, this); ++ ++ return { ++ top: top, ++ left: left, ++ right: right, ++ bottom: bottom, ++ width: right - left, ++ height: bottom - top ++ }; ++ ++ }, ++ ++ /** ++ * Trickle down of noFill ++ */ ++ noFill: function() { ++ this.children.forEach(function(child) { ++ child.noFill(); ++ }); ++ return this; ++ }, ++ ++ /** ++ * Trickle down of noStroke ++ */ ++ noStroke: function() { ++ this.children.forEach(function(child) { ++ child.noStroke(); ++ }); ++ return this; ++ }, ++ ++ /** ++ * Trickle down subdivide ++ */ ++ subdivide: function() { ++ var args = arguments; ++ this.children.forEach(function(child) { ++ child.subdivide.apply(child, args); ++ }); ++ return this; ++ }, ++ ++ flagReset: function() { ++ ++ if (this._flagAdditions) { ++ this.additions.length = 0; ++ this._flagAdditions = false; ++ } ++ ++ if (this._flagSubtractions) { ++ this.subtractions.length = 0; ++ this._flagSubtractions = false; ++ } ++ ++ this._flagOrder = this._flagMask = this._flagOpacity = false; ++ ++ Two.Shape.prototype.flagReset.call(this); ++ ++ return this; ++ ++ } ++ ++ }); ++ ++ Group.MakeObservable(Group.prototype); ++ ++ /** ++ * Helper function used to sync parent-child relationship within the ++ * `Two.Group.children` object. ++ * ++ * Set the parent of the passed object to another object ++ * and updates parent-child relationships ++ * Calling with one arguments will simply remove the parenting ++ */ ++ function replaceParent(child, newParent) { ++ ++ var parent = child.parent; ++ var index; ++ ++ if (parent === newParent) { ++ this.additions.push(child); ++ this._flagAdditions = true; ++ return; ++ } ++ ++ if (parent && parent.children.ids[child.id]) { ++ ++ index = _.indexOf(parent.children, child); ++ parent.children.splice(index, 1); ++ ++ // If we're passing from one parent to another... ++ index = _.indexOf(parent.additions, child); ++ ++ if (index >= 0) { ++ parent.additions.splice(index, 1); ++ } else { ++ parent.subtractions.push(child); ++ parent._flagSubtractions = true; ++ } ++ ++ } ++ ++ if (newParent) { ++ child.parent = newParent; ++ this.additions.push(child); ++ this._flagAdditions = true; ++ return; ++ } ++ ++ // If we're passing from one parent to another... ++ index = _.indexOf(this.additions, child); ++ ++ if (index >= 0) { ++ this.additions.splice(index, 1); ++ } else { ++ this.subtractions.push(child); ++ this._flagSubtractions = true; ++ } ++ ++ delete child.parent; ++ ++ } ++ ++})((typeof global !== 'undefined' ? global : this).Two); diff --cc debian/patches/32bit-avoid-overloading.patch index 000000000,000000000..7d906caff new file mode 100644 --- /dev/null +++ b/debian/patches/32bit-avoid-overloading.patch @@@ -1,0 -1,0 +1,23 @@@ ++Description: Avoid overloading on 32 bit architectures ++ unsigned and size_t are equivalent on 32 bit architectures, ++ so only define the size_t based overload of advance on 64 ++ bit architectures. ++ https://wiki.debian.org/ArchitectureSpecificsMemo ++Author: James Page , Bernd Zeimetz ++Forwarded: no ++ ++--- a/src/include/buffer.h +++++ b/src/include/buffer.h ++@@ -737,7 +737,12 @@ inline namespace v14_2_0 { ++ ++ void advance(int o) = delete; ++ void advance(unsigned o); +++ +++// unsigned and size_t are equivalent on 32bit architectures. +++// so casting is only needed when not on 32bit. +++#if defined(UINTPTR_MAX) && UINTPTR_MAX > 0xffffffff ++ void advance(size_t o) { advance(static_cast(o)); } +++#endif ++ void seek(unsigned o); ++ char operator*() const; ++ iterator_impl& operator++(); diff --cc debian/patches/32bit-avoid-size_t.patch index 000000000,000000000..1917577c4 new file mode 100644 --- /dev/null +++ b/debian/patches/32bit-avoid-size_t.patch @@@ -1,0 -1,0 +1,112 @@@ ++Description: Avoid use of size_t when necessary ++ On 32 bit architectures size_t is not a 64 bit type, which ++ causes comparison mismatch failures during compilation. ++Author: James Page , Bernd Zeimetz ++Forwarded: no ++ ++Index: ceph/src/osd/PrimaryLogPG.cc ++=================================================================== ++--- ceph.orig/src/osd/PrimaryLogPG.cc +++++ ceph/src/osd/PrimaryLogPG.cc ++@@ -1611,7 +1611,7 @@ int PrimaryLogPG::do_scrub_ls(MOSDOp *m, ++ ++ void PrimaryLogPG::calc_trim_to() ++ { ++- size_t target = cct->_conf->osd_min_pg_log_entries; +++ uint64_t target = cct->_conf->osd_min_pg_log_entries; ++ if (is_degraded() || ++ state_test(PG_STATE_RECOVERING | ++ PG_STATE_RECOVERY_WAIT | ++@@ -1627,15 +1627,15 @@ void PrimaryLogPG::calc_trim_to() ++ if (limit != eversion_t() && ++ limit != pg_trim_to && ++ pg_log.get_log().approx_size() > target) { ++- size_t num_to_trim = std::min(pg_log.get_log().approx_size() - target, ++- cct->_conf->osd_pg_log_trim_max); +++ uint64_t num_to_trim = std::min(pg_log.get_log().approx_size() - target, +++ cct->_conf->osd_pg_log_trim_max); ++ if (num_to_trim < cct->_conf->osd_pg_log_trim_min && ++ cct->_conf->osd_pg_log_trim_max >= cct->_conf->osd_pg_log_trim_min) { ++ return; ++ } ++ list::const_iterator it = pg_log.get_log().log.begin(); ++ eversion_t new_trim_to; ++- for (size_t i = 0; i < num_to_trim; ++i) { +++ for (uint64_t i = 0; i < num_to_trim; ++i) { ++ new_trim_to = it->version; ++ ++it; ++ if (new_trim_to > limit) { ++Index: ceph/src/os/bluestore/BlueFS.h ++=================================================================== ++--- ceph.orig/src/os/bluestore/BlueFS.h +++++ ceph/src/os/bluestore/BlueFS.h ++@@ -72,7 +72,7 @@ public: ++ * @params ++ * alloc_size - allocation unit size to check ++ */ ++- virtual size_t available_freespace(uint64_t alloc_size) = 0; +++ virtual uint64_t available_freespace(uint64_t alloc_size) = 0; ++ }; ++ ++ class BlueFSVolumeSelector { ++Index: ceph/src/os/bluestore/BlueStore.cc ++=================================================================== ++--- ceph.orig/src/os/bluestore/BlueStore.cc +++++ ceph/src/os/bluestore/BlueStore.cc ++@@ -5930,12 +5930,12 @@ int BlueStore::allocate_bluefs_freespace ++ return 0; ++ } ++ ++-size_t BlueStore::available_freespace(uint64_t alloc_size) { ++- size_t total = 0; ++- auto iterated_allocation = [&](size_t off, size_t len) { +++uint64_t BlueStore::available_freespace(uint64_t alloc_size) { +++ uint64_t total = 0; +++ auto iterated_allocation = [&](uint64_t off, uint64_t len) { ++ //only count in size that is alloc_size aligned ++- size_t dist_to_alignment; ++- size_t offset_in_block = off & (alloc_size - 1); +++ uint64_t dist_to_alignment; +++ uint64_t offset_in_block = off & (alloc_size - 1); ++ if (offset_in_block == 0) ++ dist_to_alignment = 0; ++ else ++Index: ceph/src/os/bluestore/BlueStore.h ++=================================================================== ++--- ceph.orig/src/os/bluestore/BlueStore.h +++++ ceph/src/os/bluestore/BlueStore.h ++@@ -3107,7 +3107,7 @@ private: ++ PExtentVector& extents) override { ++ return allocate_bluefs_freespace(min_size, size, &extents); ++ }; ++- size_t available_freespace(uint64_t alloc_size) override; +++ uint64_t available_freespace(uint64_t alloc_size) override; ++ ++ public: ++ struct sb_info_t { ++Index: ceph/src/common/config_values.h ++=================================================================== ++--- ceph.orig/src/common/config_values.h +++++ ceph/src/common/config_values.h ++@@ -50,7 +50,7 @@ public: ++ #define OPTION_OPT_U32(name) uint64_t name; ++ #define OPTION_OPT_U64(name) uint64_t name; ++ #define OPTION_OPT_UUID(name) uuid_d name; ++-#define OPTION_OPT_SIZE(name) size_t name; +++#define OPTION_OPT_SIZE(name) uint64_t name; ++ #define OPTION(name, ty) \ ++ public: \ ++ OPTION_##ty(name) ++Index: ceph/src/common/options.cc ++=================================================================== ++--- ceph.orig/src/common/options.cc +++++ ceph/src/common/options.cc ++@@ -189,7 +189,7 @@ int Option::parse_value( ++ } ++ *out = uuid; ++ } else if (type == Option::TYPE_SIZE) { ++- Option::size_t sz{strict_iecstrtoll(val.c_str(), error_message)}; +++ Option::size_t sz{static_cast(strict_iecstrtoll(val.c_str(), error_message))}; ++ if (!error_message->empty()) { ++ return -EINVAL; ++ } diff --cc debian/patches/CVE-2021-3979.patch index 000000000,000000000..acedafd09 new file mode 100644 --- /dev/null +++ b/debian/patches/CVE-2021-3979.patch @@@ -1,0 -1,0 +1,150 @@@ ++From: Guillaume Abrioux ++Date: Tue, 25 Jan 2022 10:25:53 +0100 ++Subject: ceph-volume: honour osd_dmcrypt_key_size option ++ ++ceph-volume doesn't honour osd_dmcrypt_key_size. ++It means the default size is always applied. ++ ++It also changes the default value in `get_key_size_from_conf()` ++ ++From cryptsetup manpage: ++ ++> For XTS mode you can optionally set a key size of 512 bits with the -s option. ++ ++Using more than 512bits will end up with the following error message: ++ ++``` ++Key size in XTS mode must be 256 or 512 bits. ++``` ++ ++Fixes: https://tracker.ceph.com/issues/54006 ++ ++Signed-off-by: Guillaume Abrioux ++(cherry picked from commit 47c33179f9a15ae95cc1579a421be89378602656) ++ ++origin: https://github.com/ceph/ceph/commit/f69339e00f582ec64b843ff58b66817975fca0d7 ++bug: https://tracker.ceph.com/issues/54006 ++--- ++ .../ceph_volume/tests/util/test_encryption.py | 41 +++++++++++++++------- ++ src/ceph-volume/ceph_volume/util/encryption.py | 34 ++++++++++++------ ++ 2 files changed, 51 insertions(+), 24 deletions(-) ++ ++diff --git a/src/ceph-volume/ceph_volume/tests/util/test_encryption.py b/src/ceph-volume/ceph_volume/tests/util/test_encryption.py ++index e1420b4..c86dc50 100644 ++--- a/src/ceph-volume/ceph_volume/tests/util/test_encryption.py +++++ b/src/ceph-volume/ceph_volume/tests/util/test_encryption.py ++@@ -1,5 +1,31 @@ ++ from ceph_volume.util import encryption +++import base64 ++ +++class TestGetKeySize(object): +++ def test_get_size_from_conf_default(self, conf_ceph_stub): +++ conf_ceph_stub(''' +++ [global] +++ fsid=asdf +++ ''') +++ assert encryption.get_key_size_from_conf() == '512' +++ +++ def test_get_size_from_conf_custom(self, conf_ceph_stub): +++ conf_ceph_stub(''' +++ [global] +++ fsid=asdf +++ [osd] +++ osd_dmcrypt_key_size=256 +++ ''') +++ assert encryption.get_key_size_from_conf() == '256' +++ +++ def test_get_size_from_conf_custom_invalid(self, conf_ceph_stub): +++ conf_ceph_stub(''' +++ [global] +++ fsid=asdf +++ [osd] +++ osd_dmcrypt_key_size=1024 +++ ''') +++ assert encryption.get_key_size_from_conf() == '512' ++ ++ class TestStatus(object): ++ ++@@ -37,17 +63,6 @@ class TestDmcryptClose(object): ++ ++ class TestDmcryptKey(object): ++ ++- def test_dmcrypt_with_default_size(self, conf_ceph_stub): ++- conf_ceph_stub('[global]\nfsid=asdf-lkjh') ++- result = encryption.create_dmcrypt_key() ++- assert len(result) == 172 ++- ++- def test_dmcrypt_with_custom_size(self, conf_ceph_stub): ++- conf_ceph_stub(''' ++- [global] ++- fsid=asdf ++- [osd] ++- osd_dmcrypt_size=8 ++- ''') +++ def test_dmcrypt(self): ++ result = encryption.create_dmcrypt_key() ++- assert len(result) == 172 +++ assert len(base64.b64decode(result)) == 128 ++diff --git a/src/ceph-volume/ceph_volume/util/encryption.py b/src/ceph-volume/ceph_volume/util/encryption.py ++index 72a0ccf..2a2c033 100644 ++--- a/src/ceph-volume/ceph_volume/util/encryption.py +++++ b/src/ceph-volume/ceph_volume/util/encryption.py ++@@ -9,21 +9,29 @@ from .disk import lsblk, device_family, get_part_entry_type ++ ++ logger = logging.getLogger(__name__) ++ ++- ++-def create_dmcrypt_key(): +++def get_key_size_from_conf(): ++ """ ++- Create the secret dm-crypt key used to decrypt a device. +++ Return the osd dmcrypt key size from config file. +++ Default is 512. ++ """ ++- # get the customizable dmcrypt key size (in bits) from ceph.conf fallback ++- # to the default of 1024 ++- dmcrypt_key_size = conf.ceph.get_safe( +++ default_key_size = '512' +++ key_size = conf.ceph.get_safe( ++ 'osd', ++ 'osd_dmcrypt_key_size', ++- default=1024, ++- ) ++- # The size of the key is defined in bits, so we must transform that ++- # value to bytes (dividing by 8) because we read in bytes, not bits ++- random_string = os.urandom(int(dmcrypt_key_size / 8)) +++ default='512') +++ +++ if key_size not in ['256', '512']: +++ logger.warning(("Invalid value set for osd_dmcrypt_key_size ({}). " +++ "Falling back to {}bits".format(key_size, default_key_size))) +++ return default_key_size +++ +++ return key_size +++ +++def create_dmcrypt_key(): +++ """ +++ Create the secret dm-crypt key (KEK) used to encrypt/decrypt the Volume Key. +++ """ +++ random_string = os.urandom(128) ++ key = base64.b64encode(random_string).decode('utf-8') ++ return key ++ ++@@ -38,6 +46,8 @@ def luks_format(key, device): ++ command = [ ++ 'cryptsetup', ++ '--batch-mode', # do not prompt +++ '--key-size', +++ get_key_size_from_conf(), ++ '--key-file', # misnomer, should be key ++ '-', # because we indicate stdin for the key here ++ 'luksFormat', ++@@ -83,6 +93,8 @@ def luks_open(key, device, mapping): ++ """ ++ command = [ ++ 'cryptsetup', +++ '--key-size', +++ get_key_size_from_conf(), ++ '--key-file', ++ '-', ++ '--allow-discards', # allow discards (aka TRIM) requests for device diff --cc debian/patches/CVE-2022-0670.patch index 000000000,000000000..b3145e886 new file mode 100644 --- /dev/null +++ b/debian/patches/CVE-2022-0670.patch @@@ -1,0 -1,0 +1,277 @@@ ++From 373a04cf734d0ed7f4f2451d99826a9517cbe651 Mon Sep 17 00:00:00 2001 ++From: Kotresh HR ++Date: Fri, 22 Jul 2022 13:23:39 +0530 ++Subject: [PATCH 1/4] mgr/volumes: Fix subvolume discover during upgrade ++ ++Fixes the subvolume discover to use the correct ++metadata file after an upgrade from legacy subvolume ++to v1. The fix makes sure, it doesn't use the ++handcrafted metadata file placed in the subvolume ++root of legacy subvolume. ++ ++Co-authored-by: Arthur Outhenin-Chalandre ++Co-authored-by: Dan van der Ster ++Co-authored-by: Ramana Raja ++Signed-off-by: Kotresh HR ++--- ++ .../fs/operations/versions/metadata_manager.py | 17 ++++++++++++++--- ++ .../fs/operations/versions/subvolume_base.py | 17 ++++++++++++++++- ++ 2 files changed, 30 insertions(+), 4 deletions(-) ++ ++--- a/src/pybind/mgr/volumes/fs/operations/versions/metadata_manager.py +++++ b/src/pybind/mgr/volumes/fs/operations/versions/metadata_manager.py ++@@ -40,16 +40,17 @@ ++ def refresh(self): ++ fd = None ++ conf_data = StringIO() +++ log.debug("opening config {0}".format(self.config_path)) ++ try: ++- log.debug("opening config {0}".format(self.config_path)) ++ fd = self.fs.open(self.config_path, os.O_RDONLY) ++ while True: ++ data = self.fs.read(fd, -1, MetadataManager.MAX_IO_BYTES) ++ if not len(data): ++ break ++ conf_data.write(data.decode('utf-8')) ++- conf_data.seek(0) ++- self.config.readfp(conf_data) +++ except UnicodeDecodeError: +++ raise MetadataMgrException(-errno.EINVAL, +++ "failed to decode, erroneous metadata config '{0}'".format(self.config_path)) ++ except cephfs.ObjectNotFound: ++ raise MetadataMgrException(-errno.ENOENT, "metadata config '{0}' not found".format(self.config_path)) ++ except cephfs.Error as e: ++@@ -58,6 +59,16 @@ ++ if fd is not None: ++ self.fs.close(fd) ++ +++ conf_data.seek(0) +++ try: +++ if sys.version_info >= (3, 2): +++ self.config.read_file(conf_data) +++ else: +++ self.config.readfp(conf_data) +++ except configparser.Error: +++ raise MetadataMgrException(-errno.EINVAL, "failed to parse, erroneous metadata config " +++ "'{0}'".format(self.config_path)) +++ ++ def flush(self): ++ # cull empty sections ++ for section in list(self.config.sections()): ++--- a/src/pybind/mgr/volumes/fs/operations/versions/subvolume_base.py +++++ b/src/pybind/mgr/volumes/fs/operations/versions/subvolume_base.py ++@@ -4,6 +4,7 @@ ++ import errno ++ import logging ++ from hashlib import md5 +++from pathlib import Path ++ ++ import cephfs ++ ++@@ -14,6 +15,7 @@ ++ from ...exception import MetadataMgrException, VolumeException ++ from .op_sm import SubvolumeOpSm ++ from .auth_metadata import AuthMetadataManager +++from .subvolume_attrs import SubvolumeStates ++ ++ log = logging.getLogger(__name__) ++ ++@@ -109,7 +111,7 @@ ++ @property ++ def state(self): ++ """ Subvolume state, one of SubvolumeStates """ ++- raise NotImplementedError +++ return SubvolumeStates.from_value(self.metadata_mgr.get_global_option(MetadataManager.GLOBAL_META_KEY_STATE)) ++ ++ @property ++ def subvol_type(self): ++@@ -121,6 +123,15 @@ ++ raise NotImplementedError ++ ++ def load_config(self): +++ try: +++ self.fs.stat(self.legacy_config_path) +++ self.legacy_mode = True +++ except cephfs.Error as e: +++ pass +++ +++ log.debug("loading config " +++ "'{0}' [mode: {1}]".format(self.subvolname, "legacy" +++ if self.legacy_mode else "new")) ++ if self.legacy_mode: ++ self.metadata_mgr = MetadataManager(self.fs, self.legacy_config_path, 0o640) ++ else: ++@@ -268,8 +279,16 @@ ++ self.fs.stat(self.base_path) ++ self.metadata_mgr.refresh() ++ log.debug("loaded subvolume '{0}'".format(self.subvolname)) +++ subvolpath = self.metadata_mgr.get_global_option(MetadataManager.GLOBAL_META_KEY_PATH) +++ # subvolume with retained snapshots has empty path, don't mistake it for +++ # fabricated metadata. +++ if (not self.legacy_mode and self.state != SubvolumeStates.STATE_RETAINED and +++ self.base_path.decode('utf-8') != str(Path(subvolpath).parent)): +++ raise MetadataMgrException(-errno.ENOENT, 'fabricated .meta') ++ except MetadataMgrException as me: ++- if me.errno == -errno.ENOENT and not self.legacy_mode: +++ if me.errno in (-errno.ENOENT, -errno.EINVAL) and not self.legacy_mode: +++ log.warn("subvolume '{0}', {1}, " +++ "assuming legacy_mode".format(self.subvolname, me.error_str)) ++ self.legacy_mode = True ++ self.load_config() ++ self.discover() ++--- a/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v1.py +++++ b/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v1.py ++@@ -667,7 +667,7 @@ ++ ++ @property ++ def state(self): ++- return SubvolumeStates.from_value(self.metadata_mgr.get_global_option(MetadataManager.GLOBAL_META_KEY_STATE)) +++ return super(SubvolumeV1, self).state ++ ++ @state.setter ++ def state(self, val): ++--- a/qa/tasks/cephfs/test_volumes.py +++++ b/qa/tasks/cephfs/test_volumes.py ++@@ -4433,3 +4433,142 @@ ++ ++ # verify trash dir is clean ++ self._wait_for_trash_empty() +++ +++ def test_malicious_metafile_on_legacy_to_v1_upgrade(self): +++ """ +++ Validate handcrafted .meta file on legacy subvol root doesn't break the system +++ on legacy subvol upgrade to v1 +++ poor man's upgrade test -- theme continues... +++ """ +++ subvol1, subvol2 = self._generate_random_subvolume_name(2) +++ +++ # emulate a old-fashioned subvolume in the default group +++ createpath1 = os.path.join(".", "volumes", "_nogroup", subvol1) +++ self.mount_a.run_shell(['mkdir', '-p', createpath1], sudo=True) +++ +++ # add required xattrs to subvolume +++ default_pool = self.mount_a.getfattr(".", "ceph.dir.layout.pool") +++ self.mount_a.setfattr(createpath1, 'ceph.dir.layout.pool', default_pool, sudo=True) +++ +++ # create v2 subvolume +++ self._fs_cmd("subvolume", "create", self.volname, subvol2) +++ +++ # Create malicious .meta file in legacy subvolume root. Copy v2 subvolume +++ # .meta into legacy subvol1's root +++ subvol2_metapath = os.path.join(".", "volumes", "_nogroup", subvol2, ".meta") +++ self.mount_a.run_shell(["cp", subvol2_metapath, createpath1], sudo=True) +++ +++ # Upgrade legacy subvol1 to v1 +++ subvolpath1 = self._fs_cmd("subvolume", "getpath", self.volname, subvol1) +++ self.assertNotEqual(subvolpath1, None) +++ subvolpath1 = subvolpath1.rstrip() +++ +++ # the subvolume path returned should not be of subvol2 from handcrafted +++ # .meta file +++ self.assertEqual(createpath1[1:], subvolpath1) +++ +++ # ensure metadata file is in legacy location, with required version v1 +++ self._assert_meta_location_and_version(self.volname, subvol1, version=1, legacy=True) +++ +++ # Authorize alice authID read-write access to subvol1. Verify it authorizes subvol1 path and not subvol2 +++ # path whose '.meta' file is copied to subvol1 root +++ authid1 = "alice" +++ self._fs_cmd("subvolume", "authorize", self.volname, subvol1, authid1) +++ +++ # Validate that the mds path added is of subvol1 and not of subvol2 +++ out = json.loads(self.fs.mon_manager.raw_cluster_cmd("auth", "get", "client.alice", "--format=json-pretty")) +++ self.assertEqual("client.alice", out[0]["entity"]) +++ self.assertEqual("allow rw path={0}".format(createpath1[1:]), out[0]["caps"]["mds"]) +++ +++ # remove subvolume +++ self._fs_cmd("subvolume", "rm", self.volname, subvol1) +++ self._fs_cmd("subvolume", "rm", self.volname, subvol2) +++ +++ # verify trash dir is clean +++ self._wait_for_trash_empty() +++ +++ def test_binary_metafile_on_legacy_to_v1_upgrade(self): +++ """ +++ Validate binary .meta file on legacy subvol root doesn't break the system +++ on legacy subvol upgrade to v1 +++ poor man's upgrade test -- theme continues... +++ """ +++ subvol = self._generate_random_subvolume_name() +++ group = self._generate_random_group_name() +++ +++ # emulate a old-fashioned subvolume -- in a custom group +++ createpath = os.path.join(".", "volumes", group, subvol) +++ self.mount_a.run_shell(['mkdir', '-p', createpath], sudo=True) +++ +++ # add required xattrs to subvolume +++ default_pool = self.mount_a.getfattr(".", "ceph.dir.layout.pool") +++ self.mount_a.setfattr(createpath, 'ceph.dir.layout.pool', default_pool, sudo=True) +++ +++ # Create unparseable binary .meta file on legacy subvol's root +++ meta_contents = os.urandom(4096) +++ meta_filepath = os.path.join(self.mount_a.mountpoint, createpath, ".meta") +++ self.mount_a.client_remote.write_file(meta_filepath, meta_contents, sudo=True) +++ +++ # Upgrade legacy subvol to v1 +++ subvolpath = self._fs_cmd("subvolume", "getpath", self.volname, subvol, group) +++ self.assertNotEqual(subvolpath, None) +++ subvolpath = subvolpath.rstrip() +++ +++ # The legacy subvolume path should be returned for subvol. +++ # Should ignore unparseable binary .meta file in subvol's root +++ self.assertEqual(createpath[1:], subvolpath) +++ +++ # ensure metadata file is in legacy location, with required version v1 +++ self._assert_meta_location_and_version(self.volname, subvol, subvol_group=group, version=1, legacy=True) +++ +++ # remove subvolume +++ self._fs_cmd("subvolume", "rm", self.volname, subvol, group) +++ +++ # verify trash dir is clean +++ self._wait_for_trash_empty() +++ +++ # remove group +++ self._fs_cmd("subvolumegroup", "rm", self.volname, group) +++ +++ def test_unparseable_metafile_on_legacy_to_v1_upgrade(self): +++ """ +++ Validate unparseable text .meta file on legacy subvol root doesn't break the system +++ on legacy subvol upgrade to v1 +++ poor man's upgrade test -- theme continues... +++ """ +++ subvol = self._generate_random_subvolume_name() +++ group = self._generate_random_group_name() +++ +++ # emulate a old-fashioned subvolume -- in a custom group +++ createpath = os.path.join(".", "volumes", group, subvol) +++ self.mount_a.run_shell(['mkdir', '-p', createpath], sudo=True) +++ +++ # add required xattrs to subvolume +++ default_pool = self.mount_a.getfattr(".", "ceph.dir.layout.pool") +++ self.mount_a.setfattr(createpath, 'ceph.dir.layout.pool', default_pool, sudo=True) +++ +++ # Create unparseable text .meta file on legacy subvol's root +++ meta_contents = "unparseable config\nfile ...\nunparseable config\nfile ...\n" +++ meta_filepath = os.path.join(self.mount_a.mountpoint, createpath, ".meta") +++ self.mount_a.client_remote.write_file(meta_filepath, meta_contents, sudo=True) +++ +++ # Upgrade legacy subvol to v1 +++ subvolpath = self._fs_cmd("subvolume", "getpath", self.volname, subvol, group) +++ self.assertNotEqual(subvolpath, None) +++ subvolpath = subvolpath.rstrip() +++ +++ # The legacy subvolume path should be returned for subvol. +++ # Should ignore unparseable binary .meta file in subvol's root +++ self.assertEqual(createpath[1:], subvolpath) +++ +++ # ensure metadata file is in legacy location, with required version v1 +++ self._assert_meta_location_and_version(self.volname, subvol, subvol_group=group, version=1, legacy=True) +++ +++ # remove subvolume +++ self._fs_cmd("subvolume", "rm", self.volname, subvol, group) +++ +++ # verify trash dir is clean +++ self._wait_for_trash_empty() +++ +++ # remove group +++ self._fs_cmd("subvolumegroup", "rm", self.volname, group) diff --cc debian/patches/CVE-2022-3650_1_ceph-crash_drop_privleges_to_run_as_ceph_user_rather_than_root.patch index 000000000,000000000..ad57f69a9 new file mode 100644 --- /dev/null +++ b/debian/patches/CVE-2022-3650_1_ceph-crash_drop_privleges_to_run_as_ceph_user_rather_than_root.patch @@@ -1,0 -1,0 +1,61 @@@ ++Description: CVE-2022-3650: ceph-crash: drop privleges to run as "ceph" user, rather than root ++ If privileges cannot be dropped, log an error and exit. This commit ++ also catches and logs exceptions when scraping the crash path, without ++ which ceph-crash would just exit if it encountered an error. ++Author: Tim Serong ++Date: Wed, 2 Nov 2022 14:27:47 +1100 ++Bug: https://tracker.ceph.com/issues/57967 ++Signed-off-by: Tim Serong ++Origin: upstream, https://github.com/ceph/ceph/commit/130c9626598bc3a75942161e6cce7c664c447382 ++Bug-Debian: https://bugs.debian.org/1024932 ++Last-Update: 2022-11-28 ++ ++--- ceph-14.2.21.orig/src/ceph-crash.in +++++ ceph-14.2.21/src/ceph-crash.in ++@@ -3,8 +3,10 @@ ++ # vim: ts=4 sw=4 smarttab expandtab ++ ++ import argparse +++import grp ++ import logging ++ import os +++import pwd ++ import socket ++ import subprocess ++ import sys ++@@ -76,7 +78,23 @@ def scrape_path(path): ++ ) ++ ++ +++def drop_privs(): +++ if os.getuid() == 0: +++ try: +++ ceph_uid = pwd.getpwnam("ceph").pw_uid +++ ceph_gid = grp.getgrnam("ceph").gr_gid +++ os.setgroups([]) +++ os.setgid(ceph_gid) +++ os.setuid(ceph_uid) +++ except Exception as e: +++ log.error(f"Unable to drop privileges: {e}") +++ sys.exit(1) +++ +++ ++ def main(): +++ # run as unprivileged ceph user +++ drop_privs() +++ ++ args = parse_args() ++ postdir = os.path.join(args.path, 'posted') ++ if args.name: ++@@ -88,7 +106,10 @@ def main(): ++ ++ log.info("monitoring path %s, delay %ds" % (args.path, args.delay * 60.0)) ++ while True: ++- scrape_path(args.path) +++ try: +++ scrape_path(args.path) +++ except Exception as e: +++ log.error(f"Error scraping {args.path}: {e}") ++ if args.delay == 0: ++ sys.exit(0) ++ time.sleep(args.delay * 60) diff --cc debian/patches/CVE-2022-3650_2_ceph-crash_fix_stderr_handling.patch index 000000000,000000000..4db4998ec new file mode 100644 --- /dev/null +++ b/debian/patches/CVE-2022-3650_2_ceph-crash_fix_stderr_handling.patch @@@ -1,0 -1,0 +1,24 @@@ ++Description: CVE-2022-3650: ceph-crash: fix stderr handling ++ Popen.communicate() returns a tuple (stdout, stderr), and stderr ++ will be of type bytes, hence the need to decode it before checking ++ if it's an empty string or not. ++Author: Tim Serong ++Date: Wed, 2 Nov 2022 14:23:20 +1100 ++Bug: a77b47eeeb5770eeefcf4619ab2105ee7a6a003e ++Signed-off-by: Tim Serong ++Bug-Debian: https://bugs.debian.org/1024932 ++Origin: upstream, https://github.com/ceph/ceph/commit/45915540559126a652f8d9d105723584cfc63439 ++Last-Update: 2022-11-28 ++ ++--- ceph-14.2.21.orig/src/ceph-crash.in +++++ ceph-14.2.21/src/ceph-crash.in ++@@ -46,7 +46,8 @@ def post_crash(path): ++ stderr=subprocess.PIPE, ++ ) ++ f = open(os.path.join(path, 'meta'), 'rb') ++- stdout, stderr = pr.communicate(input=f.read()) +++ (_, stderr) = pr.communicate(input=f.read()) +++ stderr = stderr.decode() ++ rc = pr.wait() ++ f.close() ++ if rc != 0: diff --cc debian/patches/CVE-2023-43040.patch index 000000000,000000000..f84ccb8d8 new file mode 100644 --- /dev/null +++ b/debian/patches/CVE-2023-43040.patch @@@ -1,0 -1,0 +1,51 @@@ ++From: Joshua Baergen ++Date: Wed, 17 May 2023 12:17:09 -0600 ++Subject: CVE-2023-43040 rgw: Fix bucket validation against POST policies ++ ++It's possible that user could provide a form part as a part of a POST ++object upload that uses 'bucket' as a key; in this case, it was ++overriding what was being set in the validation env (which is the real ++bucket being modified). The result of this is that a user could actually ++upload to any bucket accessible by the specified access key by matching ++the bucket in the POST policy in said POST form part. ++ ++Fix this simply by setting the bucket to the correct value after the ++POST form parts are processed, ignoring the form part above if ++specified. ++ ++bug: https://tracker.ceph.com/issues/63004 ++bug-debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1053690 ++bug-debian-security: https://deb.freexian.com/extended-lts/tracker/CVE-2023-43040 ++ ++Signed-off-by: Joshua Baergen ++origin: backport, https://github.com/ceph/ceph/commit/479976538fe8f51edfea597443ba0c0209d3f39f ++--- ++ src/rgw/rgw_rest_s3.cc | 8 ++++---- ++ 1 file changed, 4 insertions(+), 4 deletions(-) ++ ++diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc ++index f25890f..f5d1f76 100644 ++--- a/src/rgw/rgw_rest_s3.cc +++++ b/src/rgw/rgw_rest_s3.cc ++@@ -1998,10 +1998,6 @@ int RGWPostObj_ObjStore_S3::get_params() ++ ++ map_qs_metadata(s); ++ ++- ldout(s->cct, 20) << "adding bucket to policy env: " << s->bucket.name ++- << dendl; ++- env.add_var("bucket", s->bucket.name); ++- ++ bool done; ++ do { ++ struct post_form_part part; ++@@ -2052,6 +2048,10 @@ int RGWPostObj_ObjStore_S3::get_params() ++ env.add_var(part.name, part_str); ++ } while (!done); ++ +++ ldout(s->cct, 20) << "adding bucket to policy env: " << s->bucket.name +++ << dendl; +++ env.add_var("bucket", s->bucket.name); +++ ++ string object_str; ++ if (!part_str(parts, "key", &object_str)) { ++ err_msg = "Key not specified"; diff --cc debian/patches/CVE-2024-47866.patch index 000000000,000000000..c74863cf4 new file mode 100644 --- /dev/null +++ b/debian/patches/CVE-2024-47866.patch @@@ -1,0 -1,0 +1,27 @@@ ++From bef59f17293e6e93af025eba1e00646d0b1a2bf0 Mon Sep 17 00:00:00 2001 ++From: Suyash Dongre ++Date: Wed, 20 Aug 2025 23:22:41 +0530 ++Subject: [PATCH] Check if `HTTP_X_AMZ_COPY_SOURCE` header is empty ++ ++The issue was that the `HTTP_X_AMZ_COPY_SOURCE` header could be present but empty (i.e., an empty string rather than NULL). The code only checked if the pointer was not NULL, but didn't verify that the string had content. When an empty string was passed to RGWCopyObj::parse_copy_location(), it would eventually try to access name_str[0] on an empty string, causing a crash. ++ ++Fixes: https://tracker.ceph.com/issues/72669 ++ ++Signed-off-by: Suyash Dongre ++--- ++ src/rgw/rgw_op.cc | 4 +++ ++ 1 file changed, 4 insertions(+) ++ ++--- a/src/rgw/rgw_op.cc +++++ b/src/rgw/rgw_op.cc ++@@ -4830,6 +4830,10 @@ ++ params_str = url_src.substr(pos + 1); ++ } ++ +++ if (name_str.empty()) { +++ return false; +++ } +++ ++ boost::string_view dec_src{name_str}; ++ if (dec_src[0] == '/') ++ dec_src.remove_prefix(1); diff --cc debian/patches/CVE-2025-52555-1.patch index 000000000,000000000..bcc7e223f new file mode 100644 --- /dev/null +++ b/debian/patches/CVE-2025-52555-1.patch @@@ -1,0 -1,0 +1,47 @@@ ++From: Xiubo Li ++Date: Wed, 3 Apr 2024 19:02:08 +0800 ++Subject: [PATCH] client: disallow unprivileged users to escalate root ++ privileges ++ ++An unprivileged user can `chmod 777` a directory owned by root ++and gain access. Fix this bug and also add a test case for the ++same. ++ ++Signed-off-by: Xiubo Li ++Signed-off-by: Venky Shankar ++origin: backport, https://github.com/ceph/ceph/commit/b6d85b595ea7c9e0fca10d5e77a48102110fe22c ++bug-github-pull: https://github.com/ceph/ceph/pull/60314 ++bug: https://github.com/ceph/ceph/security/advisories/GHSA-89hm-qq33-2fjm ++bug-debian: https://bugs.debian.org/1108410 ++--- ++ src/client/Client.cc | 17 ++++++++++++++++- ++ 1 file changed, 16 insertions(+), 1 deletion(-) ++ ++diff --git a/src/client/Client.cc b/src/client/Client.cc ++index 22bd81d..a4eb505 100755 ++--- a/src/client/Client.cc +++++ b/src/client/Client.cc ++@@ -5448,7 +5448,22 @@ int Client::may_setattr(Inode *in, struct ceph_statx *stx, int mask, ++ } ++ ++ if (mask & CEPH_SETATTR_MODE) { ++- if (perms.uid() != 0 && perms.uid() != in->uid) +++ bool allowed = false; +++ /* +++ * Currently the kernel fuse and libfuse code is buggy and +++ * won't pass the ATTR_KILL_SUID/ATTR_KILL_SGID to ceph-fuse. +++ * But will just set the ATTR_MODE and at the same time by +++ * clearing the suid/sgid bits. +++ * +++ * Only allow unprivileged users to clear S_ISUID and S_ISUID. +++ */ +++ if ((in->mode & (S_ISUID | S_ISGID)) != (stx->stx_mode & (S_ISUID | S_ISGID)) && +++ (in->mode & ~(S_ISUID | S_ISGID)) == (stx->stx_mode & ~(S_ISUID | S_ISGID))) { +++ allowed = true; +++ } +++ uint32_t m = ~stx->stx_mode & in->mode; // mode bits removed +++ ldout(cct, 20) << __func__ << " " << *in << " = " << hex << m << dec << dendl; +++ if (perms.uid() != 0 && perms.uid() != in->uid && !allowed) ++ goto out; ++ ++ gid_t i_gid = (mask & CEPH_SETATTR_GID) ? stx->stx_gid : in->gid; diff --cc debian/patches/CVE-2025-52555-2.patch index 000000000,000000000..bd5f253f8 new file mode 100644 --- /dev/null +++ b/debian/patches/CVE-2025-52555-2.patch @@@ -1,0 -1,0 +1,65 @@@ ++From: Kefu Chai ++Date: Sat, 5 Jul 2025 16:23:36 +0800 ++Subject: [PATCH] client: prohibit unprivileged users from setting sgid/suid ++ bits ++ ++Prior to fb1b72d, unprivileged users could add mode bits as long as ++S_ISUID and S_ISGID were not included in the change. ++ ++After fb1b72d, unprivileged users were allowed to modify S_ISUID and ++S_ISGID bits only when no other mode bits were changed in the same ++operation. This inadvertently permitted unprivileged users to set ++S_ISUID and/or S_ISGID bits when they were the sole bits being modified. ++ ++This behavior should not be allowed. Unprivileged users should be ++prohibited from setting S_ISUID and/or S_ISGID bits under any ++circumstances. ++ ++This change tightens the permission check to prevent unprivileged ++users from setting these privileged bits in all cases. ++ ++Signed-off-by: Kefu Chai ++origin: backport, https://github.com/ceph/ceph/commit/7028ed21138522495df1e9f8b01195a3c43d47ff ++bug-debian: https://bugs.debian.org/1109470 ++bug: https://github.com/ceph/ceph/pull/64356 ++--- ++ src/client/Client.cc | 19 ++++++++++--------- ++ 1 file changed, 10 insertions(+), 9 deletions(-) ++ ++diff --git a/src/client/Client.cc b/src/client/Client.cc ++index a4eb505..7153f1c 100755 ++--- a/src/client/Client.cc +++++ b/src/client/Client.cc ++@@ -5448,22 +5448,23 @@ int Client::may_setattr(Inode *in, struct ceph_statx *stx, int mask, ++ } ++ ++ if (mask & CEPH_SETATTR_MODE) { ++- bool allowed = false; ++ /* ++ * Currently the kernel fuse and libfuse code is buggy and ++ * won't pass the ATTR_KILL_SUID/ATTR_KILL_SGID to ceph-fuse. ++ * But will just set the ATTR_MODE and at the same time by ++ * clearing the suid/sgid bits. ++ * ++- * Only allow unprivileged users to clear S_ISUID and S_ISUID. +++ * Only allow unprivileged users to clear S_ISUID and S_ISGID. ++ */ ++- if ((in->mode & (S_ISUID | S_ISGID)) != (stx->stx_mode & (S_ISUID | S_ISGID)) && ++- (in->mode & ~(S_ISUID | S_ISGID)) == (stx->stx_mode & ~(S_ISUID | S_ISGID))) { ++- allowed = true; ++- } ++- uint32_t m = ~stx->stx_mode & in->mode; // mode bits removed ++- ldout(cct, 20) << __func__ << " " << *in << " = " << hex << m << dec << dendl; ++- if (perms.uid() != 0 && perms.uid() != in->uid && !allowed) +++ uint32_t removed_bits = ~stx->stx_mode & in->mode; +++ uint32_t added_bits = ~in->mode & stx->stx_mode; +++ bool clearing_suid_sgid = ( +++ // no new bits added +++ added_bits == 0 && +++ // only suid/suid bits removed +++ (removed_bits & ~(S_ISUID | S_ISGID)) == 0); +++ ldout(cct, 20) << __func__ << " " << *in << " = " << hex << removed_bits << dec << dendl; +++ if (perms.uid() != 0 && perms.uid() != in->uid && !clearing_suid_sgid) ++ goto out; ++ ++ gid_t i_gid = (mask & CEPH_SETATTR_GID) ? stx->stx_gid : in->gid; diff --cc debian/patches/add-option-to-disable-ceph-dencoder.patch index 000000000,000000000..a9cf20577 new file mode 100644 --- /dev/null +++ b/debian/patches/add-option-to-disable-ceph-dencoder.patch @@@ -1,0 -1,0 +1,11 @@@ ++Index: ceph/src/tools/CMakeLists.txt ++=================================================================== ++--- ceph.orig/src/tools/CMakeLists.txt +++++ ceph/src/tools/CMakeLists.txt ++@@ -126,4 +126,6 @@ if(WITH_RBD) ++ endif() ++ endif(WITH_RBD) ++ +++if(NOT DISABLE_DENCODER) ++ add_subdirectory(ceph-dencoder) +++endif(DISABLE_DENCODER) diff --cc debian/patches/allow-bgp-to-host.patch index 000000000,000000000..6c76dda39 new file mode 100644 --- /dev/null +++ b/debian/patches/allow-bgp-to-host.patch @@@ -1,0 -1,0 +1,18 @@@ ++Description: allow BGP-to-the-host style binding ++Author: Thomas Goirand ++Forwarded: no ++Last-Update: 2021-04-21 ++ ++diff --git a/src/common/ipaddr.cc b/src/common/ipaddr.cc ++index 0abf7f20ca..ce81e7e735 100644 ++--- a/src/common/ipaddr.cc +++++ b/src/common/ipaddr.cc ++@@ -56,7 +56,7 @@ const struct ifaddrs *find_ipv4_in_subnet(const struct ifaddrs *addrs, ++ if (addrs->ifa_addr == NULL) ++ continue; ++ ++- if (strcmp(addrs->ifa_name, "lo") == 0 || boost::starts_with(addrs->ifa_name, "lo:")) +++ if (boost::starts_with(addrs->ifa_name, "lo:")) ++ continue; ++ ++ if (numa_node >= 0 && !match_numa_node(addrs->ifa_name, numa_node)) diff --cc debian/patches/another-cmakelists-fix.patch index 000000000,000000000..75abf2339 new file mode 100644 --- /dev/null +++ b/debian/patches/another-cmakelists-fix.patch @@@ -1,0 -1,0 +1,35 @@@ ++Description: Another cmakelists fix ++ This fixes the last Boost 1.74 compatibility problems. ++Author: Thomas Goirand ++Forwarded: no ++Last-Update: 2021-01-08 ++ ++Index: ceph/CMakeLists.txt ++=================================================================== ++--- ceph.orig/CMakeLists.txt +++++ ceph/CMakeLists.txt ++@@ -30,6 +30,9 @@ endif() ++ if(POLICY CMP0093) ++ cmake_policy(SET CMP0093 NEW) ++ endif() +++if(POLICY CMP0093) +++ cmake_policy(SET CMP0093 NEW) +++endif() ++ list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules/") ++ ++ if(CMAKE_SYSTEM_NAME MATCHES "Linux") ++Index: ceph/src/rgw/CMakeLists.txt ++=================================================================== ++--- ceph.orig/src/rgw/CMakeLists.txt +++++ ceph/src/rgw/CMakeLists.txt ++@@ -23,6 +23,10 @@ if(Boost_VERSION VERSION_GREATER 1.73) ++ add_definitions(-DBOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT) ++ endif() ++ +++if(Boost_VERSION VERSION_GREATER_EQUAL 1.74) +++ add_definitions(-DBOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT) +++endif() +++ ++ set(librgw_common_srcs ++ services/svc_finisher.cc ++ services/svc_notify.cc diff --cc debian/patches/bluefs-use-uint64_t-for-len.patch index 000000000,000000000..fb629b88a new file mode 100644 --- /dev/null +++ b/debian/patches/bluefs-use-uint64_t-for-len.patch @@@ -1,0 -1,0 +1,58 @@@ ++From 10a953afc8f803e50c96354470fb114b33e62599 Mon Sep 17 00:00:00 2001 ++From: Kefu Chai ++Date: Fri, 28 Jun 2019 11:35:54 +0800 ++Subject: [PATCH] os/bluestore/BlueFS: use uint64_t for `len` ++ ++change the type of parameter `len` of `BlueFS::_read_random()` from ++`size_t` to `uint64_t`. ++ ++i think the type of `size_t` comes from ++`rocksdb::RandomAccessFile::Read(uint64_t offset, size_t n, ++rocksdb::Slice* result, char* scratch)`. and when we implement this ++method, we continued using `n`'s type. but, we are using it with ++`std::min()`, for instance, where the template parameter type deduction ++fails if the lhs and rhs parameters' types are different. so probaly the ++better solution is to use `uint64_t` directly to avoid the the cast and ++specializing the template. ++ ++Signed-off-by: Kefu Chai ++--- ++ src/os/bluestore/BlueFS.cc | 4 ++-- ++ src/os/bluestore/BlueFS.h | 2 +- ++ 2 files changed, 3 insertions(+), 3 deletions(-) ++ ++Index: ceph/src/os/bluestore/BlueFS.cc ++=================================================================== ++--- ceph.orig/src/os/bluestore/BlueFS.cc +++++ ceph/src/os/bluestore/BlueFS.cc ++@@ -1678,7 +1678,7 @@ void BlueFS::_drop_link(FileRef file) ++ int64_t BlueFS::_read_random( ++ FileReader *h, ///< [in] read from here ++ uint64_t off, ///< [in] offset ++- size_t len, ///< [in] this many bytes +++ uint64_t len, ///< [in] this many bytes ++ char *out) ///< [out] optional: or copy it here ++ { ++ auto* buf = &h->buf; ++@@ -1710,7 +1710,7 @@ int64_t BlueFS::_read_random( ++ uint64_t x_off = 0; ++ auto p = h->file->fnode.seek(off, &x_off); ++ ceph_assert(p != h->file->fnode.extents.end()); ++- uint64_t l = std::min(p->length - x_off, static_cast(len)); +++ uint64_t l = std::min(p->length - x_off, len); ++ //hard cap to 1GB ++ l = std::min(l, uint64_t(1) << 30); ++ dout(20) << __func__ << " read random 0x" ++Index: ceph/src/os/bluestore/BlueFS.h ++=================================================================== ++--- ceph.orig/src/os/bluestore/BlueFS.h +++++ ceph/src/os/bluestore/BlueFS.h ++@@ -422,7 +422,7 @@ private: ++ int64_t _read_random( ++ FileReader *h, ///< [in] read from here ++ uint64_t offset, ///< [in] offset ++- size_t len, ///< [in] this many bytes +++ uint64_t len, ///< [in] this many bytes ++ char *out); ///< [out] optional: or copy it here ++ ++ void _invalidate_cache(FileRef f, uint64_t offset, uint64_t length); diff --cc debian/patches/civetweb-755-1.8-somaxconn-configurable.patch index 000000000,000000000..8313b3ece new file mode 100644 --- /dev/null +++ b/debian/patches/civetweb-755-1.8-somaxconn-configurable.patch @@@ -1,0 -1,0 +1,53 @@@ ++Description: Makes SOMAXCONN user-configurable. ++Author: Jesse Williamson ++Origin: upstream, https://github.com/civetweb/civetweb/pull/776/commits/febab7dc38c9671577603425c54c20f841e27f97 ++Bug: https://github.com/civetweb/civetweb/issues/775 ++Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/ceph/+bug/1838109 ++--- ++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ ++--- a/src/civetweb/src/civetweb.c +++++ b/src/civetweb/src/civetweb.c ++@@ -1541,10 +1541,6 @@ typedef int socklen_t; ++ #define MSG_NOSIGNAL (0) ++ #endif ++ ++-#if !defined(SOMAXCONN) ++-#define SOMAXCONN (100) ++-#endif ++- ++ /* Size of the accepted socket queue */ ++ #if !defined(MGSQLEN) ++ #define MGSQLEN (20) ++@@ -2063,6 +2059,7 @@ enum { ++ SSL_CERTIFICATE, ++ SSL_CERTIFICATE_CHAIN, ++ NUM_THREADS, +++ SO_MAX_CONNECTIONS, ++ RUN_AS_USER, ++ URL_REWRITE_PATTERN, ++ HIDE_FILES, ++@@ -2165,6 +2162,7 @@ static struct mg_option config_options[] ++ {"ssl_certificate", CONFIG_TYPE_FILE, NULL}, ++ {"ssl_certificate_chain", CONFIG_TYPE_FILE, NULL}, ++ {"num_threads", CONFIG_TYPE_NUMBER, "50"}, +++ {"max_connections", CONFIG_TYPE_NUMBER, "100"}, ++ {"run_as_user", CONFIG_TYPE_STRING, NULL}, ++ {"url_rewrite_patterns", CONFIG_TYPE_STRING_LIST, NULL}, ++ {"hide_files_patterns", CONFIG_TYPE_EXT_PATTERN, NULL}, ++@@ -13340,7 +13338,15 @@ set_ports_option(struct mg_context *ctx) ++ continue; ++ } ++ ++- if (listen(so.sock, SOMAXCONN) != 0) { +++ char *p = ctx->config[SO_MAX_CONNECTIONS]; +++ long opt_max_connections = strtol(p, NULL, 10); +++ if(opt_max_connections > INT_MAX || opt_max_connections < 1) { +++ mg_cry(fc(ctx), +++ "max_connections value \"%s\" is invalid", p); +++ continue; +++ } +++ +++ if (listen(so.sock, (int)opt_max_connections) != 0) { ++ ++ mg_cry(fc(ctx), ++ "cannot listen to %.*s: %d (%s)", diff --cc debian/patches/civetweb-755-1.8-somaxconn-configurable_conf.patch index 000000000,000000000..e8e3b0b0f new file mode 100644 --- /dev/null +++ b/debian/patches/civetweb-755-1.8-somaxconn-configurable_conf.patch @@@ -1,0 -1,0 +1,18 @@@ ++Description: Adds max_connections to reference configuration. ++Author: Jesse Williamson ++Origin: upstream, https://github.com/civetweb/civetweb/pull/776/commits/3b8eb36676f70d06f8918ccf62029207c49cdda0 ++Bug: https://github.com/civetweb/civetweb/issues/775 ++Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/ceph/+bug/1838109 ++--- ++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ ++--- a/src/civetweb/resources/civetweb.conf +++++ b/src/civetweb/resources/civetweb.conf ++@@ -8,6 +8,8 @@ ++ document_root . ++ listening_ports 8080 ++ +++#so_max_connections 100 +++ ++ # cgi_pattern **.cgi$|**.pl$|**.php$ ++ # cgi_environment ++ # put_delete_auth_file diff --cc debian/patches/civetweb-755-1.8-somaxconn-configurable_test.patch index 000000000,000000000..cdd27b5ab new file mode 100644 --- /dev/null +++ b/debian/patches/civetweb-755-1.8-somaxconn-configurable_test.patch @@@ -1,0 -1,0 +1,17 @@@ ++Description: Adds max_connections to test display. ++Author: Jesse Williamson ++Origin: upstream, https://github.com/civetweb/civetweb/pull/776/commits/3b8eb36676f70d06f8918ccf62029207c49cdda0 ++Bug: https://github.com/civetweb/civetweb/issues/775 ++Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/ceph/+bug/1838109 ++--- ++This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ ++--- a/src/civetweb/test/page3.ssjs +++++ b/src/civetweb/test/page3.ssjs ++@@ -21,6 +21,7 @@ opts = [ ++ "document_root", ++ "ssl_certificate", ++ "num_threads", +++"max_connections", ++ "run_as_user", ++ "url_rewrite_patterns", ++ "hide_files_patterns", diff --cc debian/patches/cmake_add_1.74_to_known_versions.patch index 000000000,000000000..004788918 new file mode 100644 --- /dev/null +++ b/debian/patches/cmake_add_1.74_to_known_versions.patch @@@ -1,0 -1,0 +1,52 @@@ ++Description: cmake: add 1.74 to known versions ++Author: Kefu Chai ++Bug-Debian: https://bugs.debian.org/977243 ++Origin: upstream, https://github.com/ceph/ceph/commit/b6a94da6149e50bdd43752919d7c01b04c59f79e.patch ++Last-Update: 2020-12-13 ++ ++--- ceph-14.2.15.orig/cmake/modules/FindBoost.cmake +++++ ceph-14.2.15/cmake/modules/FindBoost.cmake ++@@ -437,10 +437,23 @@ if (NOT Boost_NO_BOOST_CMAKE) ++ endif() ++ endif() ++ +++ set(_boost_FIND_PACKAGE_ARGS "") +++ if(Boost_NO_SYSTEM_PATHS) +++ list(APPEND _boost_FIND_PACKAGE_ARGS NO_CMAKE_SYSTEM_PATH NO_SYSTEM_ENVIRONMENT_PATH) +++ endif() +++ ++ # Do the same find_package call but look specifically for the CMake version. ++ # Note that args are passed in the Boost_FIND_xxxxx variables, so there is no ++ # need to delegate them to this find_package call. ++- find_package(Boost QUIET NO_MODULE) +++ cmake_policy(PUSH) +++ if(BOOST_ROOT AND NOT Boost_ROOT) +++ if(POLICY CMP0074) +++ cmake_policy(SET CMP0074 NEW) +++ endif() +++ set(Boost_ROOT "${BOOST_ROOT}") +++ endif() +++ find_package(Boost QUIET NO_MODULE ${_boost_FIND_PACKAGE_ARGS}) +++ cmake_policy(POP) ++ mark_as_advanced(Boost_DIR) ++ ++ # If we found a boost cmake package, then we're done. Print out what we found. ++@@ -1157,7 +1170,7 @@ function(_Boost_COMPONENT_DEPENDENCIES c ++ set(_Boost_TIMER_DEPENDENCIES chrono) ++ set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono date_time atomic) ++ set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) ++- if(NOT Boost_VERSION_STRING VERSION_LESS 1.73.0) +++ if(NOT Boost_VERSION_STRING VERSION_LESS 1.75.0) ++ message(WARNING "New Boost version may have incorrect or missing dependencies and imported targets") ++ endif() ++ endif() ++@@ -1429,7 +1442,8 @@ else() ++ # _Boost_COMPONENT_HEADERS. See the instructions at the top of ++ # _Boost_COMPONENT_DEPENDENCIES. ++ set(_Boost_KNOWN_VERSIONS ${Boost_ADDITIONAL_VERSIONS} ++- "1.72.0" "1.72" "1.71.0" "1.71" "1.70.0" "1.70" "1.69.0" "1.69" +++ "1.74.0" "1.74" +++ "1.73.0" "1.73" "1.72.0" "1.72" "1.71.0" "1.71" "1.70.0" "1.70" "1.69.0" "1.69" ++ "1.68.0" "1.68" "1.67.0" "1.67" "1.66.0" "1.66" "1.65.1" "1.65.0" "1.65" ++ "1.64.0" "1.64" "1.63.0" "1.63" "1.62.0" "1.62" "1.61.0" "1.61" "1.60.0" "1.60" ++ "1.59.0" "1.59" "1.58.0" "1.58" "1.57.0" "1.57" "1.56.0" "1.56" "1.55.0" "1.55" diff --cc debian/patches/cmake_define_BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT_for_Boost.Asio_users.patch index 000000000,000000000..2c792ce67 new file mode 100644 --- /dev/null +++ b/debian/patches/cmake_define_BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT_for_Boost.Asio_users.patch @@@ -1,0 -1,0 +1,30 @@@ ++Description: cmake: define BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT for ++ Boost.Asio users ++ . ++ see also ++ https://www.boost.org/doc/libs/1_74_0/doc/html/boost_asio/std_executors.html#boost_asio.std_executors.polymorphic_i_o_executor ++ . ++ we could use `asio::any_io_executor` later on though for better ++ performance. ++ . ++ also, define CMP0093, so FindBoost reports Boost_VERSION in x.y.z ++ format. it is simpler to use `VERSION_GREATER_EQUAL` to compare its ++ version with 1.74 instead of its C macro version ("107000"). ++Signed-off-by: Kefu Chai ++Author: Kefu Chai ++Origin: upstream, https://github.com/ceph/ceph/commit/3d708219092d0e89a1434c30ffc8a4999f062cc0.patch ++Bug-Debian: https://bugs.debian.org/977243 ++Last-Update: 2021-03-24 ++ ++--- ceph-14.2.15.orig/CMakeLists.txt 2020-12-14 09:42:50.543215302 +0100 +++++ ceph-14.2.15/CMakeLists.txt 2020-12-14 09:44:07.827084724 +0100 ++@@ -21,6 +21,9 @@ ++ if(POLICY CMP0051) ++ cmake_policy(SET CMP0051 NEW) ++ endif() +++if(POLICY CMP0074) +++ cmake_policy(SET CMP0074 NEW) +++endif() ++ if(POLICY CMP0075) ++ cmake_policy(SET CMP0075 NEW) ++ endif() diff --cc debian/patches/debian-armel-armhf-buildflags.patch index 000000000,000000000..e9a450aa5 new file mode 100644 --- /dev/null +++ b/debian/patches/debian-armel-armhf-buildflags.patch @@@ -1,0 -1,0 +1,45 @@@ ++--- a/cmake/modules/SIMDExt.cmake +++++ b/cmake/modules/SIMDExt.cmake ++@@ -40,11 +40,14 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch ++ ++ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm|ARM") ++ set(HAVE_ARM 1) ++- CHECK_C_COMPILER_FLAG(-mfpu=neon HAVE_ARM_NEON) ++- if(HAVE_ARM_NEON) ++- set(SIMD_COMPILE_FLAGS "${SIMD_COMPILE_FLAGS} -mfpu=neon") +++ if(CMAKE_LIBRARY_ARCHITECTURE EQUAL "arm-linux-gnueabi") +++ set(SIMD_COMPILE_FLAGS "${SIMD_COMPILE_FLAGS} --with-arch=armv5te --with-float=soft") +++ else() +++ CHECK_C_COMPILER_FLAG(-mfpu=neon HAVE_ARM_NEON) +++ if(HAVE_ARM_NEON) +++ set(SIMD_COMPILE_FLAGS "${SIMD_COMPILE_FLAGS} -mfpu=neon") +++ endif() ++ endif() ++- ++ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "i386|i686|amd64|x86_64|AMD64") ++ set(HAVE_INTEL 1) ++ if(CMAKE_SYSTEM_PROCESSOR MATCHES "i686|amd64|x86_64|AMD64") ++--- a/src/erasure-code/jerasure/gf-complete/m4/ax_ext.m4 +++++ b/src/erasure-code/jerasure/gf-complete/m4/ax_ext.m4 ++@@ -19,10 +19,17 @@ AC_DEFUN([AX_EXT], ++ ;; ++ ++ arm*) ++- AC_CACHE_CHECK([whether NEON is enabled], [ax_cv_have_neon_ext], [ax_cv_have_neon_ext=yes]) ++- if test "$ax_cv_have_neon_ext" = yes; then ++- AX_CHECK_COMPILE_FLAG(-mfpu=neon, [SIMD_FLAGS="$SIMD_FLAGS -mfpu=neon -DARM_NEON"], [ax_cv_have_neon_ext=no]) ++- fi +++ case $host_cpu in +++ arm-linux-gnueabi) +++ AX_CHECK_COMPILE_FLAG(-mfpu=soft, [SIMD_FLAGS="$SIMD_FLAGS -mfpu=soft -march=armv5te"], [ax_cv_have_neon_ext=no]) +++ ;; +++ *) +++ AC_CACHE_CHECK([whether NEON is enabled], [ax_cv_have_neon_ext], [ax_cv_have_neon_ext=yes]) +++ if test "$ax_cv_have_neon_ext" = yes; then +++ AX_CHECK_COMPILE_FLAG(-mfpu=neon, [SIMD_FLAGS="$SIMD_FLAGS -mfpu=neon -DARM_NEON"], [ax_cv_have_neon_ext=no]) +++ fi +++ ;; +++ esac ++ ;; ++ ++ powerpc*) diff --cc debian/patches/disable-crypto.patch index 000000000,000000000..7cd126829 new file mode 100644 --- /dev/null +++ b/debian/patches/disable-crypto.patch @@@ -1,0 -1,0 +1,16 @@@ ++Index: ceph/src/os/CMakeLists.txt ++=================================================================== ++--- ceph.orig/src/os/CMakeLists.txt +++++ ceph/src/os/CMakeLists.txt ++@@ -110,8 +110,9 @@ endif() ++ target_link_libraries(os kv) ++ ++ add_dependencies(os compressor_plugins) ++-add_dependencies(os crypto_plugins) ++- +++if(HAVE_INTEL AND HAVE_BETTER_YASM_ELF64 AND (NOT APPLE)) +++ add_dependencies(os crypto_plugins) +++endif() ++ ++ if(WITH_BLUESTORE) ++ add_executable(ceph-bluestore-tool diff --cc debian/patches/fix-bash-completion-location index 000000000,000000000..916ff8a79 new file mode 100644 --- /dev/null +++ b/debian/patches/fix-bash-completion-location @@@ -1,0 -1,0 +1,9 @@@ ++--- a/src/bash_completion/CMakeLists.txt +++++ b/src/bash_completion/CMakeLists.txt ++@@ -11,5 +11,5 @@ if(WITH_RADOSGW) ++ endif() ++ ++ install(FILES ${completions} ++- DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/bash_completion.d) +++ DESTINATION /usr/share/bash-completion/completions) ++ diff --cc debian/patches/fix-ceph-osd-systemd-target.patch index 000000000,000000000..9f42077ba new file mode 100644 --- /dev/null +++ b/debian/patches/fix-ceph-osd-systemd-target.patch @@@ -1,0 -1,0 +1,17 @@@ ++Description: Fix systemd ceph-osd.target ++ This helps when rebooting. ++Author: Thomas Goirand ++Forwarded: no ++Last-Update: 2021-01-28 ++ ++--- ceph-14.2.16.orig/systemd/ceph-osd.target +++++ ceph-14.2.16/systemd/ceph-osd.target ++@@ -1,7 +1,7 @@ ++ [Unit] ++ Description=ceph target allowing to start/stop all ceph-osd@.service instances at once ++ PartOf=ceph.target ++-After=ceph-mon.target +++After=ceph-mon.target systemd-udev-settle.service ++ Before=ceph.target ++ Wants=ceph.target ceph-mon.target ++ diff --cc debian/patches/make-ceph-python-3.9-aware.patch index 000000000,000000000..67e55ea7b new file mode 100644 --- /dev/null +++ b/debian/patches/make-ceph-python-3.9-aware.patch @@@ -1,0 -1,0 +1,28 @@@ ++Description: Make Ceph Python 3.9 aware ++ Add versions of interpreters Ceph didn't know about. ++Author: Thomas Goirand ++Forwarded: no ++Last-Update: 2020-11-28 ++ ++--- ceph-14.2.15.orig/cmake/modules/FindPython3Interp.cmake +++++ ceph-14.2.15/cmake/modules/FindPython3Interp.cmake ++@@ -69,7 +69,7 @@ ++ ++ unset(_Python3_NAMES) ++ ++-set(_PYTHON3_VERSIONS 3.6 3.5 3.4 3.3 3.2 3.1 3.0) +++set(_PYTHON3_VERSIONS 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) ++ ++ if(Python3Interp_FIND_VERSION) ++ if(Python3Interp_FIND_VERSION_COUNT GREATER 1) ++--- ceph-14.2.15.orig/cmake/modules/FindPython3Libs.cmake +++++ ceph-14.2.15/cmake/modules/FindPython3Libs.cmake ++@@ -101,7 +101,7 @@ endif() ++ # To avoid picking up the system Python.h pre-maturely. ++ set(CMAKE_FIND_FRAMEWORK LAST) ++ ++-set(_PYTHON3_VERSIONS 3.6 3.5 3.4 3.3 3.2 3.1 3.0) +++set(_PYTHON3_VERSIONS 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) ++ ++ if(Python3Libs_FIND_VERSION) ++ if(Python3Libs_FIND_VERSION_COUNT GREATER 1) diff --cc debian/patches/mds-purgequeue-use_uint64_t.patch index 000000000,000000000..8797da213 new file mode 100644 --- /dev/null +++ b/debian/patches/mds-purgequeue-use_uint64_t.patch @@@ -1,0 -1,0 +1,31 @@@ ++Index: ceph/src/mds/PurgeQueue.cc ++=================================================================== ++--- ceph.orig/src/mds/PurgeQueue.cc +++++ ceph/src/mds/PurgeQueue.cc ++@@ -499,7 +499,7 @@ void PurgeQueue::_execute_item( ++ ++ in_flight[expire_to] = item; ++ logger->set(l_pq_executing, in_flight.size()); ++- files_high_water = std::max(files_high_water, in_flight.size()); +++ files_high_water = std::max(files_high_water, static_cast(in_flight.size())); ++ logger->set(l_pq_executing_high_water, files_high_water); ++ auto ops = _calculate_ops(item); ++ ops_in_flight += ops; ++@@ -577,7 +577,7 @@ void PurgeQueue::_execute_item( ++ logger->set(l_pq_executing_ops_high_water, ops_high_water); ++ in_flight.erase(expire_to); ++ logger->set(l_pq_executing, in_flight.size()); ++- files_high_water = std::max(files_high_water, in_flight.size()); +++ files_high_water = std::max(files_high_water, static_cast(in_flight.size())); ++ logger->set(l_pq_executing_high_water, files_high_water); ++ return; ++ } ++@@ -654,7 +654,7 @@ void PurgeQueue::_execute_item_complete( ++ ++ in_flight.erase(iter); ++ logger->set(l_pq_executing, in_flight.size()); ++- files_high_water = std::max(files_high_water, in_flight.size()); +++ files_high_water = std::max(files_high_water, static_cast(in_flight.size())); ++ logger->set(l_pq_executing_high_water, files_high_water); ++ dout(10) << "in_flight.size() now " << in_flight.size() << dendl; ++ diff --cc debian/patches/riscv64-link-pthread.patch index 000000000,000000000..5d4a72d49 new file mode 100644 --- /dev/null +++ b/debian/patches/riscv64-link-pthread.patch @@@ -1,0 -1,0 +1,16 @@@ ++Description: Link with -pthread instead of -lpthread to fix FTBFS on riscv64 ++Forwarded: no ++Last-Update: 2020-03-01 ++ ++Index: ceph/CMakeLists.txt ++=================================================================== ++--- ceph.orig/CMakeLists.txt +++++ ceph/CMakeLists.txt ++@@ -31,6 +31,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_S ++ ++ if(CMAKE_SYSTEM_NAME MATCHES "Linux") ++ set(LINUX ON) +++ set(THREADS_PREFER_PTHREAD_FLAG ON) ++ FIND_PACKAGE(Threads) ++ elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") ++ set(FREEBSD ON) diff --cc debian/patches/series index 000000000,000000000..be2fc3057 new file mode 100644 --- /dev/null +++ b/debian/patches/series @@@ -1,0 -1,0 +1,27 @@@ ++update-java-source-target-flags.patch ++disable-crypto.patch ++32bit-avoid-overloading.patch ++32bit-avoid-size_t.patch ++civetweb-755-1.8-somaxconn-configurable_conf.patch ++civetweb-755-1.8-somaxconn-configurable.patch ++civetweb-755-1.8-somaxconn-configurable_test.patch ++bluefs-use-uint64_t-for-len.patch ++debian-armel-armhf-buildflags.patch ++fix-bash-completion-location ++add-option-to-disable-ceph-dencoder.patch ++riscv64-link-pthread.patch ++mds-purgequeue-use_uint64_t.patch ++make-ceph-python-3.9-aware.patch ++cmake_define_BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT_for_Boost.Asio_users.patch ++cmake_add_1.74_to_known_versions.patch ++another-cmakelists-fix.patch ++fix-ceph-osd-systemd-target.patch ++allow-bgp-to-host.patch ++CVE-2022-3650_1_ceph-crash_drop_privleges_to_run_as_ceph_user_rather_than_root.patch ++CVE-2022-3650_2_ceph-crash_fix_stderr_handling.patch ++CVE-2021-3979.patch ++CVE-2023-43040.patch ++CVE-2025-52555-1.patch ++CVE-2025-52555-2.patch ++CVE-2022-0670.patch ++CVE-2024-47866.patch diff --cc debian/patches/update-java-source-target-flags.patch index 000000000,000000000..6c8b79228 new file mode 100644 --- /dev/null +++ b/debian/patches/update-java-source-target-flags.patch @@@ -1,0 -1,0 +1,23 @@@ ++Description: use --release 7 instead of -source/-target ++ Instead of -source/-target ceph should be build with --release for OpenJDK 9 ++ or later so that the bootclasspath is also set, as per JEP-247, otherwise it ++ risks incurring into binary incompatibility when run with an earlier OpenJDK. ++ OpenJDK 11 minimum compatibility release has been updated to 7. ++Author: Tiago Stürmer Daitx ++Bug-Ubuntu: https://launchpad.net/bugs/1756854 ++Bug-Ubuntu: https://launchpad.net/bugs/1766998 ++Forwarded: no ++Last-Update: 2018-04-24 ++--- ++ ++--- a/src/java/CMakeLists.txt +++++ b/src/java/CMakeLists.txt ++@@ -21,7 +21,7 @@ set(java_srcs ++ # warning: [options] bootstrap class path not set in conjunction with -source 1.7 ++ # as per ++ # https://blogs.oracle.com/darcy/entry/bootclasspath_older_source ++-set(CMAKE_JAVA_COMPILE_FLAGS "-source" "1.8" "-target" "1.8" "-Xlint:-options") +++set(CMAKE_JAVA_COMPILE_FLAGS "--release" "7" "-Xlint:-options") ++ set(jni_header_dir "${CMAKE_CURRENT_BINARY_DIR}/native") ++ if(CMAKE_VERSION VERSION_LESS 3.11) ++ set(CMAKE_JAVA_COMPILE_FLAGS ${CMAKE_JAVA_COMPILE_FLAGS} "-h" ${jni_header_dir}) diff --cc debian/python3-ceph.lintian-overrides index 000000000,000000000..fd0d214b0 new file mode 100644 --- /dev/null +++ b/debian/python3-ceph.lintian-overrides @@@ -1,0 -1,0 +1,1 @@@ ++python3-ceph: empty-binary-package diff --cc debian/radosgw.install index 9bb764937,000000000..329ea0e48 mode 100644,000000..100644 --- a/debian/radosgw.install +++ b/debian/radosgw.install @@@ -1,10 -1,0 +1,6 @@@ +lib/systemd/system/ceph-radosgw* - usr/bin/ceph-diff-sorted +usr/bin/radosgw +usr/bin/radosgw-es +usr/bin/radosgw-object-expirer +usr/bin/radosgw-token - usr/bin/rgw-orphan-list - usr/share/man/man8/ceph-diff-sorted.8 +usr/share/man/man8/radosgw.8 - usr/share/man/man8/rgw-orphan-list.8 diff --cc debian/radosgw.lintian-overrides index 000000000,000000000..5c5e06b8e new file mode 100644 --- /dev/null +++ b/debian/radosgw.lintian-overrides @@@ -1,0 -1,0 +1,8 @@@ ++# Ceph upstart configuration's don't have init.d equivalents ++radosgw: init.d-script-not-marked-as-conffile etc/init.d/radosgw-all-starter ++radosgw: init.d-script-not-included-in-package etc/init.d/radosgw-all-starter ++radosgw: init.d-script-not-marked-as-conffile etc/init.d/radosgw-instance ++radosgw: init.d-script-not-included-in-package etc/init.d/radosgw-instance ++radosgw: init.d-script-not-marked-as-conffile etc/init.d/radosgw-all ++radosgw: init.d-script-not-included-in-package etc/init.d/radosgw-all ++radosgw: omitted-systemd-service-for-init.d-script radosgw diff --cc debian/radosgw.postinst index 07e3ec30b,000000000..3f1551269 mode 100644,000000..100644 --- a/debian/radosgw.postinst +++ b/debian/radosgw.postinst @@@ -1,57 -1,0 +1,17 @@@ +#!/bin/sh - # vim: set noet ts=8: - # postinst script for radosgw - # - # see: dh_installdeb(1) + +set -e + - # summary of how this script can be called: - # - # postinst configure - # old-postinst abort-upgrade - # conflictor's-postinst abort-remove in-favour - # postinst abort-remove - # deconfigured's-postinst abort-deconfigure in-favour [ ] - # - # The current action is to simply remove the mistakenly-added - # /etc/init/ceph.conf file; this could be done in any of these cases, - # although technically it will leave the system in a different state - # than the original install that included that file. So instead we - # only remove on "configure", since that's the only time we know we're - # successful in installing a newer package than the erroneous version. - - # for details, see http://www.debian.org/doc/debian-policy/ or - # the debian-policy package - - [ -f "/etc/default/ceph" ] && . /etc/default/ceph - [ -z "$SERVER_USER" ] && SERVER_USER=ceph - [ -z "$SERVER_GROUP" ] && SERVER_GROUP=ceph - - case "$1" in - configure) - [ -x /sbin/start ] && start radosgw-all || : - - if ! dpkg-statoverride --list /var/lib/ceph/radosgw >/dev/null - then - chown $SERVER_USER:$SERVER_GROUP /var/lib/ceph/radosgw ++if [ "${1}" = "configure" ] ; then ++ [ -f "/etc/default/ceph" ] && . /etc/default/ceph ++ [ -z "$SERVER_USER" ] && SERVER_USER=ceph ++ [ -z "$SERVER_GROUP" ] && SERVER_GROUP=ceph ++ if ! dpkg-statoverride --list /var/lib/ceph/radosgw >/dev/null; then ++ chown $SERVER_USER:$SERVER_GROUP /var/lib/ceph/radosgw + fi - ;; - abort-upgrade|abort-remove|abort-deconfigure) - : - ;; - - *) - echo "postinst called with unknown argument \`$1'" >&2 - exit 1 - ;; - esac - - # dh_installdeb will replace this with shell code automatically - # generated by other debhelper scripts. - ++fi +#DEBHELPER# + +exit 0 + + diff --cc debian/radosgw.prerm index 4120fb627,000000000..0288ab77b mode 100644,000000..100644 --- a/debian/radosgw.prerm +++ b/debian/radosgw.prerm @@@ -1,23 -1,0 +1,22 @@@ +#!/bin/sh +# vim: set noet ts=8: + +set -e + +case "$1" in + remove) - [ -x /sbin/stop ] && stop radosgw-all || true + invoke-rc.d radosgw stop || { + RESULT=$? + if [ $RESULT != 100 ]; then + exit $RESULT + fi + } + ;; + + *) + ;; +esac + +#DEBHELPER# + +exit 0 diff --cc debian/rest-bench.install index 000000000,000000000..8535f20d5 new file mode 100644 --- /dev/null +++ b/debian/rest-bench.install @@@ -1,0 -1,0 +1,1 @@@ ++usr/bin/rest-bench diff --cc debian/rules index 70355c987,000000000..c712fd5d1 mode 100755,000000..100755 --- a/debian/rules +++ b/debian/rules @@@ -1,141 -1,0 +1,167 @@@ +#!/usr/bin/make -f +# -*- makefile -*- - export DH_VERBOSE=1 - export DESTDIR=$(CURDIR)/debian/tmp ++#export DH_VERBOSE=1 + ++DEB_HOST_ARCH_BITS ?= $(shell dpkg-architecture -qDEB_HOST_ARCH_BITS) ++export DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH) +export DEB_HOST_ARCH ?= $(shell dpkg-architecture -qDEB_HOST_ARCH) - ifneq (,$(findstring WITH_STATIC_LIBSTDCXX,$(CEPH_EXTRA_CMAKE_ARGS))) - # dh_auto_build sets LDFLAGS with `dpkg-buildflags --get LDFLAGS` on ubuntu, - # which makes the application aborts when the shared library throws - # exception, so strip this linker option, - # see http://tracker.ceph.com/issues/25209 - export DEB_LDFLAGS_MAINT_STRIP = -Wl,-Bsymbolic-functions ++ ++# support ccache for faster build ++# cmake uses /usr/bin/c* ++ifeq (yes,$(findstring yes,$(shell test -L /usr/lib/ccache/c++ && test -L /usr/lib/ccache/cc && echo -n yes))) ++ extraopts += -DWITH_CCACHE=ON ++endif ++ ++# try to save even more memory on some architectures ++# see #849657 for hints. ++# Reduce size of debug symbols to fix FTBFS due to the ++# 2GB/3GB address space limits on 32bit ++ifeq (32,$(DEB_HOST_ARCH_BITS)) ++ export DEB_CFLAGS_MAINT_APPEND = -g1 ++ export DEB_CXXFLAGS_MAINT_APPEND = -g1 ++endif ++ ++# we don't have NEON on armel. ++ifeq ($(DEB_HOST_ARCH),armel) ++ extraopts += -DHAVE_ARM_NEON=0 ++endif ++ ++# disable ceph-dencoder on 32bit except i386 to avoid g++ oom ++ifneq (,$(filter $(DEB_HOST_ARCH), armel armhf hppa m68k mips mipsel powerpc sh4 x32)) ++ extraopts += -DDISABLE_DENCODER=1 ++endif ++ ++ifeq ($(shell dpkg-vendor --is Ubuntu && echo yes) $(DEB_HOST_ARCH), yes i386) ++ skip_packages = -Nceph -Nceph-base -Nceph-mds -Nceph-mgr -Nceph-mon -Nceph-osd +endif + - extraopts += -DWITH_OCF=ON -DWITH_LTTNG=ON - extraopts += -DWITH_PYTHON3=ON -DWITH_MGR_DASHBOARD_FRONTEND=OFF ++# minimise needless linking and link to libatomic ++# The last is needed because long long atomic operations are not directly ++# supported by all processor architectures ++export DEB_LDFLAGS_MAINT_APPEND= -Wl,--as-needed -latomic ++ ++# Enable hardening ++export DEB_BUILD_MAINT_OPTIONS = hardening=+all ++ ++export DESTDIR=$(CURDIR)/debian/tmp ++ ++export JAVA_HOME=/usr/lib/jvm/default-java ++## Set JAVAC to prevent FTBFS due to incorrect use of 'gcj' if found (see "m4/ac_prog_javac.m4"). ++export JAVAC=javac ++ ++extraopts += -DWITH_OCF=ON -DWITH_NSS=ON -DWITH_PYTHON3=ON -DWITH_DEBUG=ON ++extraopts += -DWITH_PYTHON2=OFF -DMGR_PYTHON_VERSION=3 +extraopts += -DWITH_CEPHFS_JAVA=ON +extraopts += -DWITH_CEPHFS_SHELL=ON - extraopts += -DWITH_SYSTEMD=ON -DCEPH_SYSTEMD_ENV_DIR=/etc/default - # assumes that ceph is exmpt from multiarch support, so we override the libdir. - extraopts += -DCMAKE_INSTALL_LIBDIR=/usr/lib ++extraopts += -DWITH_TESTS=OFF ++extraopts += -DWITH_SYSTEM_BOOST=ON ++extraopts += -DWITH_LTTNG=OFF -DWITH_EMBEDDED=OFF +extraopts += -DCMAKE_INSTALL_LIBEXECDIR=/usr/lib ++extraopts += -DWITH_MGR_DASHBOARD_FRONTEND=OFF ++extraopts += -DWITH_SYSTEMD=ON -DCEPH_SYSTEMD_ENV_DIR=/etc/default +extraopts += -DCMAKE_INSTALL_SYSCONFDIR=/etc +extraopts += -DCMAKE_INSTALL_SYSTEMD_SERVICEDIR=/lib/systemd/system ++ +ifneq (,$(filter parallel=%,$(DEB_BUILD_OPTIONS))) + NUMJOBS = $(patsubst parallel=%,%,$(filter parallel=%,$(DEB_BUILD_OPTIONS))) + extraopts += -DBOOST_J=$(NUMJOBS) +endif + - ifneq (,$(filter $(DEB_HOST_ARCH), arm armel armhf arm64 i386 amd64 mips mipsel powerpc ppc64)) - # libboost_context only support the archs above - extraopts += -DWITH_BOOST_CONTEXT=ON - else ++ifneq (,$(filter $(DEB_HOST_ARCH),s390x mips64el ia64 m68k ppc64 riscv64 sh4 sparc64 x32 alpha)) ++ # beast depends on libboost_{context,coroutine} which is not supported on s390x + extraopts += -DWITH_BOOST_CONTEXT=OFF ++else ++ extraopts += -DWITH_BOOST_CONTEXT=ON +endif + ++# Disable SPDK as it generates a build which is no compatible ++# with older CPU's which are still supported by Ubuntu. ++extraopts += -DWITH_SPDK=OFF ++ ++MAX_PARALLEL ?= $(shell ./debian/calc-max-parallel.sh) ++ +%: - dh $@ --buildsystem=cmake --with javahelper,python2,python3,systemd --parallel ++ dh $@ --buildsystem=cmake --with javahelper,python3,systemd $(MAX_PARALLEL) + +override_dh_auto_configure: + env | sort - dh_auto_configure --buildsystem=cmake -- $(extraopts) $(CEPH_EXTRA_CMAKE_ARGS) - - override_dh_auto_build: - dh_auto_build --buildsystem=cmake - cp src/init-radosgw debian/radosgw.init - - override_dh_auto_clean: - dh_auto_clean --buildsystem=cmake - rm -f debian/radosgw.init debian/ceph.logrotate ++ dh_auto_configure --buildsystem=cmake -- $(extraopts) + +override_dh_auto_install: + dh_auto_install --buildsystem=cmake --destdir=$(DESTDIR) ++ if [ ! -f $(DESTDIR)/usr/bin/ceph-dencoder ]; then \ ++ cp debian/workarounds/ceph-dencoder-oom $(DESTDIR)/usr/bin/ceph-dencoder ;\ ++ chmod 755 $(DESTDIR)/usr/bin/ceph-dencoder ;\ ++ fi + install -D -m 644 udev/50-rbd.rules $(DESTDIR)/lib/udev/rules.d/50-rbd.rules + install -D -m 644 src/etc-rbdmap $(DESTDIR)/etc/ceph/rbdmap + install -D -m 644 etc/sysctl/90-ceph-osd.conf $(DESTDIR)/etc/sysctl.d/30-ceph-osd.conf - install -D -m 440 sudoers.d/ceph-osd-smartctl $(DESTDIR)/etc/sudoers.d/ceph-osd-smartctl ++ install -D -m 600 sudoers.d/ceph-osd-smartctl $(DESTDIR)/etc/sudoers.d/ceph-osd-smartctl ++ # NOTE: ensure that any versioned erasure coding test code is dropped ++ # from the package install - package ships unversioned modules. ++ rm -f $(CURDIR)/debian/tmp/usr/lib/*/ceph/erasure-code/libec_*.so.* ++ find $(CURDIR)/debian/tmp/usr/lib/*/ceph/erasure-code -type l -delete || : ++ + +# doc/changelog is a directory, which confuses dh_installchangelogs +override_dh_installchangelogs: + dh_installchangelogs --exclude doc/changelog + - override_dh_installdocs: - +override_dh_installlogrotate: + cp src/logrotate.conf debian/ceph-common.logrotate + dh_installlogrotate -pceph-common + ++ +override_dh_installinit: ++ cp src/init-radosgw debian/radosgw.init ++ dh_installinit --no-start ++ dh_installinit -pceph-common --name=rbdmap --no-start ++ dh_installinit -pceph-base --name ceph --no-start + # install the systemd stuff manually since we have funny service names - install -d -m0755 debian/ceph-common/etc/default - install -m0644 etc/default/ceph debian/ceph-common/etc/default/ ++ # and need to update the paths in all of the files post install ++ # systemd:ceph-common + install -d -m0755 debian/ceph-common/usr/lib/tmpfiles.d + install -m 0644 -D systemd/ceph.tmpfiles.d debian/ceph-common/usr/lib/tmpfiles.d/ceph.conf - - dh_installinit -p ceph-base --name ceph --no-start - dh_installinit -p radosgw --no-start - - # NOTE: execute systemd helpers so they pickup dh_install'ed units and targets - dh_systemd_enable - dh_systemd_start --no-restart-on-upgrade ++ # NOTE(jamespage): Install previous ceph-mon service from packaging for upgrades ++ install -d -m0755 debian/ceph-mon/lib/systemd/system ++ install -m0644 debian/lib-systemd/system/ceph-mon.service debian/ceph-mon/lib/systemd/system ++ # Ensure Debian/Ubuntu specific systemd units are NOT automatically enabled and started ++ # Enable systemd targets only ++ dh_systemd_enable -Xceph-mon.service -Xceph-osd.service -X ceph-mds.service ++ # Start systemd targets only ++ dh_systemd_start --no-stop-on-upgrade --no-restart-after-upgrade + +override_dh_systemd_enable: + # systemd enable done as part of dh_installinit + +override_dh_systemd_start: + # systemd start done as part of dh_installinit + - override_dh_strip: - dh_strip -pceph-mds --dbg-package=ceph-mds-dbg - dh_strip -pceph-fuse --dbg-package=ceph-fuse-dbg - dh_strip -pceph-mgr --dbg-package=ceph-mgr-dbg - dh_strip -pceph-mon --dbg-package=ceph-mon-dbg - dh_strip -pceph-osd --dbg-package=ceph-osd-dbg - dh_strip -pceph-base --dbg-package=ceph-base-dbg - dh_strip -pceph-fuse --dbg-package=ceph-fuse-dbg - dh_strip -prbd-fuse --dbg-package=rbd-fuse-dbg - dh_strip -prbd-mirror --dbg-package=rbd-mirror-dbg - dh_strip -prbd-nbd --dbg-package=rbd-nbd-dbg - dh_strip -pceph-common --dbg-package=ceph-common-dbg - dh_strip -plibrados2 --dbg-package=librados2-dbg - dh_strip -plibradosstriper1 --dbg-package=libradosstriper1-dbg - dh_strip -plibrbd1 --dbg-package=librbd1-dbg - dh_strip -plibcephfs2 --dbg-package=libcephfs2-dbg - dh_strip -plibrgw2 --dbg-package=librgw2-dbg - dh_strip -pradosgw --dbg-package=radosgw-dbg - dh_strip -pceph-test --dbg-package=ceph-test-dbg - dh_strip -ppython-rados --dbg-package=python-rados-dbg - dh_strip -ppython3-rados --dbg-package=python3-rados-dbg - dh_strip -ppython-rbd --dbg-package=python-rbd-dbg - dh_strip -ppython3-rbd --dbg-package=python3-rbd-dbg - dh_strip -ppython-rgw --dbg-package=python-rgw-dbg - dh_strip -ppython3-rgw --dbg-package=python3-rgw-dbg - dh_strip -ppython-cephfs --dbg-package=python-cephfs-dbg - dh_strip -ppython3-cephfs --dbg-package=python3-cephfs-dbg ++override_dh_makeshlibs: ++ # exclude jni libraries in libcephfs-jni to avoid pointless ldconfig ++ # calls in maintainer scripts; exclude private erasure-code plugins. ++ dh_makeshlibs -V -X/usr/lib/jni -X/usr/lib/$(DEB_HOST_MULTIARCH)/ceph/erasure-code ++ ++override_dh_auto_test: ++ # do not run tests + +override_dh_shlibdeps: + dh_shlibdeps -a --exclude=erasure-code --exclude=rados-classes --exclude=compressor + - override_dh_python2: - for binding in rados cephfs rbd rgw; do \ - dh_python2 -p python-$$binding; \ - done - dh_python2 -p ceph-common - dh_python2 -p ceph-base - dh_python2 -p ceph-osd - dh_python2 -p ceph-mgr - # batch-compile, and set up for delete, all the module files - dh_python2 -p ceph-mgr usr/lib/ceph/mgr - +override_dh_python3: + for binding in rados cephfs rbd rgw; do \ - dh_python3 -p python3-$$binding; \ - done - dh_python3 -p python3-ceph-argparse - dh_python3 -p cephfs-shell ++ dh_python3 -p python3-$$binding --shebang=/usr/bin/python3; \ ++ done ++ dh_python3 -p python3-ceph-argparse --shebang=/usr/bin/python3 ++ dh_python3 -p ceph-common --shebang=/usr/bin/python3 ++ dh_python3 -p ceph-base --shebang=/usr/bin/python3 ++ dh_python3 -p ceph-osd --shebang=/usr/bin/python3 ++ dh_python3 -p ceph-mgr --shebang=/usr/bin/python3 ++ dh_python3 -p cephfs-shell --shebang=/usr/bin/python3 + - # do not run tests - override_dh_auto_test: ++override_dh_builddeb: ++ dh_builddeb ${skip_packages} ++ ++override_dh_gencontrol: ++ dh_gencontrol ${skip_packages} + - .PHONY: override_dh_autoreconf override_dh_auto_configure override_dh_auto_build override_dh_auto_clean override_dh_auto_install override_dh_installdocs override_dh_installlogrotate override_dh_installinit override_dh_systemd_start override_dh_strip override_dh_auto_test diff --cc debian/source.lintian-overrides index 000000000,000000000..6a4dc1a74 new file mode 100644 --- /dev/null +++ b/debian/source.lintian-overrides @@@ -1,0 -1,0 +1,3 @@@ ++# This is a false positive: upstream is shipping both the compiled ++# and the source version of the .js files. ++ceph source: source-is-missing diff --cc debian/source/format index d3827e75a,000000000..163aaf8d8 mode 100644,000000..100644 --- a/debian/source/format +++ b/debian/source/format @@@ -1,1 -1,0 +1,1 @@@ - 1.0 ++3.0 (quilt) diff --cc debian/source/lintian-overrides index 000000000,000000000..f3cb221b9 new file mode 100644 --- /dev/null +++ b/debian/source/lintian-overrides @@@ -1,0 -1,0 +1,11 @@@ ++# we don't ship thse thirdparty libs here, afaics. ++# license.txt is outdated but fixing it causes the same error ++# come up for the patch, so... ++ceph source: license-problem-json-evil src/rapidjson/license.txt ++ ++# pybind js source is in src/pybind/mgr/dashboard/frontend/src ++ceph source: source-is-missing src/pybind/mgr/dashboard/frontend/dist/*.js ++ ++# regression test file is actually shipped with source and build files ++ceph source: source-contains-prebuilt-ms-help-file src/boost/tools/boost_install/test/iostreams/zlib-1.2.11/contrib/dotzlib/DotZLib.chm ++ceph source: source-contains-prebuilt-ms-help-file src/boost/libs/beast/test/extern/zlib-1.2.11/contrib/dotzlib/DotZLib.chm diff --cc debian/source/options index 000000000,000000000..d029bb02e new file mode 100644 --- /dev/null +++ b/debian/source/options @@@ -1,0 -1,0 +1,11 @@@ ++extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googlemock/msvc/20\d\d/gmock\.sln" ++extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googlemock/msvc/20\d\d/gmock.*vcproj" ++extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googlemock/msvc/20\d\d/gmock.*vsprops" ++extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googlemock/msvc/20\d\d/gmock.*vcxproj" ++extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googlemock/msvc/20\d\d/gmock_config.props" ++extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googletest/codegear/gtest.*\.cbproj" ++extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googletest/codegear/gtest_all\.cc" ++extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googletest/codegear/gtest_link\.cc" ++extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googletest/codegear/gtest\.groupproj" ++extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googletest/msvc/gtest.*\.vcproj" ++extend-diff-ignore = ".*src/rapidjson/thirdparty/gtest/googletest/msvc/gtest.*\.sln" diff --cc debian/tests/build-rados index 000000000,000000000..c62999251 new file mode 100755 --- /dev/null +++ b/debian/tests/build-rados @@@ -1,0 -1,0 +1,31 @@@ ++#!/bin/sh ++# autopkgtest check: Build and run a program against librados2 to ++# validate that headers are installed and libraries exists ++ ++set -e ++ ++WORKDIR=$(mktemp -d) ++trap "rm -rf $WORKDIR" 0 INT QUIT ABRT PIPE TERM ++cd $WORKDIR ++cat < radostest.c ++#include ++ ++int ++main(void) ++{ ++ int err; ++ rados_t cluster; ++ ++ err = rados_create(&cluster, NULL); ++ if (err < 0) { ++ return (1); ++ } ++ return(0); ++} ++EOF ++ ++gcc -o radostest radostest.c -lrados ++echo "build: OK" ++[ -x radostest ] ++./radostest ++echo "run: OK" diff --cc debian/tests/build-rbd index 000000000,000000000..5ad6f8823 new file mode 100755 --- /dev/null +++ b/debian/tests/build-rbd @@@ -1,0 -1,0 +1,24 @@@ ++#!/bin/sh ++# autopkgtest check: Build and run a program against librbd1 to ++# validate that headers are installed and libraries exists ++ ++set -e ++ ++WORKDIR=$(mktemp -d) ++trap "rm -rf $WORKDIR" 0 INT QUIT ABRT PIPE TERM ++cd $WORKDIR ++cat < rbdtest.c ++#include ++ ++int ++main(void) ++{ ++ return(0); ++} ++EOF ++ ++gcc -o rbdtest rbdtest.c -lrbd ++echo "build: OK" ++[ -x rbdtest ] ++./rbdtest ++echo "run: OK" diff --cc debian/tests/ceph-client index 000000000,000000000..c693a5698 new file mode 100755 --- /dev/null +++ b/debian/tests/ceph-client @@@ -1,0 -1,0 +1,11 @@@ ++#!/bin/bash ++ ++set -e ++ ++CLIENTS=('ceph') ++ ++for client in "${CLIENTS[@]}"; do ++ echo -n "Testing client $client: " ++ $client -v 2>&1 > /dev/null ++ echo "OK" ++done diff --cc debian/tests/control index 000000000,000000000..372a057d0 new file mode 100644 --- /dev/null +++ b/debian/tests/control @@@ -1,0 -1,0 +1,9 @@@ ++Tests: ceph-client build-rados build-rbd python-ceph ++Depends: ++ build-essential, ++ ceph-common, ++ librados-dev, ++ librbd-dev, ++ python3-rados, ++ python3-rbd, ++Restrictions: needs-root diff --cc debian/tests/python-ceph index 000000000,000000000..006dbe63d new file mode 100755 --- /dev/null +++ b/debian/tests/python-ceph @@@ -1,0 -1,0 +1,7 @@@ ++#!/usr/bin/python3 ++ ++# Test that rbd and rados can be imported OK ++import rbd ++import rados ++ ++print("python-ceph: OK") diff --cc debian/udev/95-ceph-osd-lvm.rules index 000000000,000000000..00feae51f new file mode 100644 --- /dev/null +++ b/debian/udev/95-ceph-osd-lvm.rules @@@ -1,0 -1,0 +1,13 @@@ ++# OSD LVM layout example ++# VG prefix: ceph- ++# LV prefix: osd- ++ACTION=="add", SUBSYSTEM=="block", \ ++ ENV{DEVTYPE}=="disk", \ ++ ENV{DM_LV_NAME}=="osd-*", \ ++ ENV{DM_VG_NAME}=="ceph-*", \ ++ OWNER:="ceph", GROUP:="ceph", MODE:="660" ++ACTION=="change", SUBSYSTEM=="block", \ ++ ENV{DEVTYPE}=="disk", \ ++ ENV{DM_LV_NAME}=="osd-*", \ ++ ENV{DM_VG_NAME}=="ceph-*", \ ++ OWNER="ceph", GROUP="ceph", MODE="660" diff --cc debian/watch index ec9ecbf5d,000000000..279f3d5f1 mode 100644,000000..100644 --- a/debian/watch +++ b/debian/watch @@@ -1,2 -1,0 +1,3 @@@ +version=3 - https://download.ceph.com/tarballs/ceph-(\d.*)\.tar\.gz ++opts="uversionmangle=s/-/~/,dversionmangle=s/\+dfsg\d*$//" \ ++ http://download.ceph.com/tarballs/ceph-(\d.*)\.tar\.gz diff --cc debian/workarounds/ceph-dencoder-oom index 000000000,000000000..872021328 new file mode 100644 --- /dev/null +++ b/debian/workarounds/ceph-dencoder-oom @@@ -1,0 -1,0 +1,7 @@@ ++#!/bin/bash ++ ++echo "Unfortunately it is not possible to compile ceph-dencoder" 1>&2 ++echo "on this architecture, see bug #947886." 1>&2 ++echo "" 1>&2 ++exit 1 ++